blog.immatt

I'm Matt - You're here for my blog!

Setting up a Pi-Hole on a Raspberry Pi Model A in 2024

It’s 2024.

You’re looking back at 2023 as a blur.

How many different Internet providers did you go through?

It could have been 3 – it could have been 300.

It was 3.

3 different Internet service providers, all with their own unique and special modem/router combo of varying feature sets – some good, some okay, and some terrible.

We began 2023 with Spectrum, our ISP since moving to this house in 2015. While Spectrum provided pretty decent service, outages and signal issues posed more issues then one would generally prefer, and their speed offerings weren’t quite up to snuff in our area.

Next came Verizon 5G Home Internet. As soon as this service became available in my area, I signed up. All in all, Verizon’s 5G Home Internet was pretty solid. The router was feature rich and not having to have a signal/supply line is pretty cool – just plug in the box near a window that has good 5G access and you’re set to enjoy a pretty consistent 300Mbs/60Mbs connection.

Enter AT&T Fiber. As good as Verizon’s service was for me, fiber is king – and so when I saw the install trucks running lines in my neighborhood, I just knew I had to get on the waiting list. So far AT&T Fiber has been great. It’s notably faster than my already snappy Verizon 5G. Since install, I’ve not had a single outage – despite significant weather events occurring, which would normally knock me offline at least temporarily. One things that’s not great though is the router functionality. And don’t get me wrong, it’s not terrible – it’s just missing some things I’ve become accustomed to when using Verizon’s modem/router, such as an internal DNS Server.

What’s this post about again? Oh, yeah. ISPs…

Wait. No. This isn’t a post about ISPs.

This is a post about setting up a Pi-Hole on one of the first ever released Raspberry Pis, the Raspberry Pi Model A Revision 2.0 from back in 2011, in 2024.

But… Why?

Like many of my fellow technophiles, I have a lot of ‘junk’ electronics laying around – filling boxes, drawers, cabinets, shelfs and even whole closets…

“Why not just through them out” -my wife, constantly.

“I might need them one day” -me, constantly.

So… Here I am… In need of a DNS server and in the possession of many pieces of aging technology without an assigned purpose.

Enter the 13yr old Raspberry Pi.

Having not touched a Raspberry Pi in quite some time, I figured I’d do a bit of Googling to figure out the path of least resistance for getting this Pi back into the land of the living. After a couple of clicks, I find that there’s now an official Raspberry Pi Imager, so that seems a good place to start.

For my purposes, since I’m going to just be installing Pi-Hole, I want a minimalistic OS as my base. Since this Raspberry Pi is so old, I’m limited on what OSes I can easily install via the Imager – so I’m opting for ‘Raspberry Pi OS Legacy 32-bit Lite’, as I don’t require the full desktop experience and figure those system resources could better be utilized powering the Pi-Hole.

When going through the imaging process, I was greeted by some prompts that made me happy I’d opted to go with the official Raspberry Pi Imager – the ability to adjust some setting for my OS during the flashing process, so that I don’t have to manually connect to my Raspberry Pi in order to get things like SSH going:

Once the flash completed, I inserted my newly imaged SD card into the fatty SD slot on my Raspberry Pi and connected it via ETH to my Orbi base station, and powered it up to see if it would come online… I mean.. This thing is basically an antique – and, as such, likely hasn’t even had electricity racing through its circuits in nearly a decade…

Based on the lovely glow, it either has power….or it has caught fire:

Let’s see if we can SSH in:

Saaaaa-weet.

This isn’t a tutorial, and is instead intended to be more of a show and tell – so I will spare you the play by play on setting things up, as there are a number of excellent tutorials that already exist for this purpose. But rest assured, I followed one or two of them, fumbled along with the setup (not really tho – the Pi-Hole installer UI holds your hands and makes it pretty easy).

After the Pi-hole install completed, I then ensured I had it a static IP assigned in my router (should have arguably done this from the start, but…well…yeah…) before configuring my router to use that IP (the Pi-Hole’s IP) as my primary DNS server…

BOOM!

Old dogs can be taught new tricks, it would seem.

I’ll admit – I somewhat entered into this experiment figuring that I’d have to redo it all over again on a newer device than the Raspberry Pi Model A Revision 2.0 from back in 2011, but this thing is proving to be sufficiently snappy thus far.

I have things basically configured as the default right now – leveraging both traffic filtering, logging and local DNS… I’ll run things as they are for a bit and tweak according to my needs. This said, I’m happy to report that after putting things through its paces for a normal evening, everything appears to be working as expected – streaming services and other connected services are working as expected out the box, with ads being blocked left and right!

Anywhos! Thanks for checking in!

-Matt

Running Windows 11 on Apple M1 Silicone under MacOS

Wait. What? Say huh?

Okay. Whatever. If you’re reading this, I have to assume you read the title of the post and so know why you’re here – so let’s dig in.

I recently purchased a new Apple MacBook Pro for things and stuff. Because I like to waste more time doing things that feel like work rather than doing rewarding actual work, I figured I should figure out how to most quickly violate the laws of nature – and what faster path to condemnation than to install Windows 11 on Apple silicone (specifically, the M1 Pro).

Note: This post is going to discuss how to get the ARM build of Windows 11 running on Apple silicone – a point worth noting, as there may be some limitations.

Up first, we need to install UTM – https://mac.getutm.app/

I won’t get into the specifics of installing UTM – if you’re here, on my tech blog, reading a super nerdy post about running an OS on a platform that god herself has forbidden, I assume you know how to drag the little app icon thingy into the application folders icon thingy… Cool – installed.

Up next, you’re going to need a Windows 11 image to run, right? Here, we’re going to use the Windows Insider Preview (which requires an Insiders account – so get to registering in the likely event that your Apple Fangirl/Fanboy butt doesn’t already have one) – https://www.microsoft.com/en-us/software-download/windowsinsiderpreviewARM64

Now go get a tasty beverage while you wait for that Windows 11 download to complete – I averaged 2MBs on my ~300MBs connection…

Downloaded? Cool-cool-cool. Now we need to get it running…

Let’s open up UTM.

With UTM open, you’re going to want to click on ‘Create a New Virtual Machine’:

And then ‘Virtualize’:

And then ‘Linux’:

Wait… No.. Not ‘Linux’ – ‘Windows’, dummy… Gah….

Ensure you click ‘Import VHDX Image’ and then click ‘Browse…’ and browse to the Windows 11 VHDX that took one million years to download the last time you tried to complete this tutorial before ultimately walking away and forgetting that you were trying to get Windows running on your MBP M1/M2:

Special Note: Previously it would seem you needed to manually download the ‘SPICE’, similarly to VirtualBox Extensions, but this now seems to be handled automagically (assuming you’re not a dumb-dumb and accidentally deselect ‘Install drivers and SPICE tools’ like I may or may not have the first time I did this).

Click ‘Continue’ to proceed to configuring your memory and CPU allocation – I’m opting for defaults because I want Windows 11 to run like garbage and to be painfully slow:

Then optionally configure a share between your host (MacOS) and guest (Windows 11). I recommend this as how else are you going to easily screw up your line endings while working in Git repos?:

After clicking ‘Continue’, you should be presented with this totes helpful Summary screen – just click ‘Save’:

Watch the super cool spinny:

Now we’re ready to start the setup/install process – click that little ‘Play Button’ in the middle of the preview or up at the top at the toolbar (or just figure it out – maybe recite a little spell or say a little prayer, I dunno…):

Things seem to be happening:

“ARE YOU READY?!?!” -Jonathan Davis:

Ah.. A ‘moment’ – such a fun imaginary increment of time…but then again, isn’t all time imaginary? But for real – maybe go do something else…this is going to take a while…:

Some PLURAL moments later, you should be dumped out into the Windows setup – just proceed until you can’t anymore (e.g. see next step for network workaround):

After clicking Continue a couple times and Skip, you’ll likely find yourself stuck on the following screen:

Now what? Let’s just move on… Press ‘Shift+F10’ (so fn+shift+F10) to open a command prompt and then type ”OOBE\BYPASSNRO” and press [ENTER] (okay…okay… ‘return’):

The Windows 11 image should restart and the setup process should restart – once you progress to the Network screen, you should now see a new option – ‘I don’t have internet’ (anyone notice how poorly styled that is? It’s like MS doesn’t like this option or something…):

Click ‘I don’t have internet’ – and then click ‘Continue with limited setup’ on the following screen:

Accept the License Agreement and then setup your account on the following screens:

There are a handful of screens not captured in screenshots here – BECAUSE I’M LAZY!!!! Just ‘Next’, ‘Next’, ‘Next’ until you’re done – I opted out of all optional settings (which I simply mention in case you do something different and then have an error)…

After a few moments and a few friendly progress screens, the Windows setup should be complete – and you then should find yourself looking at your shiny new Windows 11 desktop!:

But we’re not quite done yet – if you try to do much, you’re likely to see what I mean:

We still need to get network ready…

First, you’ll want to click on the little CD/Disk icon to access the ‘Drive image options’:

Then select ‘Install Windows Guest Tools…’:

You may get a notification that the image is already mounted – in which case, sorry for wasting your time, you can carry on…

Next you will want to access the mounted disk, which is mounted as a virtual CD/DVD drive: – run ‘spice-guest-tools-…’ by double clicking it:

Yes:

Next and then ‘I agree’ should get you installing:

Reboot now:

After the reboot completes, you should find your Windows 11 install a bit more useful:

Up next in our series, we’ll look at setting up WSL2 under Windows 11 under MacOS 🤣

I hope that you’ve found this of value in angering your respective gods! Happy Dev’ing!

-Matt

TIL: Browser window/tab/frame/iframe intercommunication via BroadcastChannel Web API

I was today years old when I learned of the BroadcastChannel Web API, which facilitates intercommunication between windows, tabs, frames and iframes on the same domain (thanks to Cory House on Twitter).

At a glance, the API is surprisingly simple, so I figured why not give it a spin.

After consulting the ever-valuable MDN Web Docs on the topic, I had a BroadcastChannel spun up in a matter of seconds – here’s the overview and demonstration of it in action.

To setup my playground, I simply opened up a new Chrome window – and then 2 tabs, which I pointed to my blog here. At this point, all that’s left to be done is to new up an instance of BroadcastChannel in both tabs, setup an onmessage event listener in the receiver tab, and then to broadcast a message from the sender tab.

Here we have our ‘sender’ tab in tab 1 – where we’ve instantiated a new BroadcastChannel, setup an event listener, and then posted a new message.
Note: We could have skipped setting up the listener here, but I wanted to demonstrate that the sending tab doesn’t display the posted message.
Here we have our ‘reciever’ tab in tab 2 – where we’ve instantiated a new BroadcastChannel and setup an event listener. As we can see in the last visible debugger frame, we recieved our ‘WOW!’ message from tab 1.
Note: The ‘WOW!’ message was sent from tab 1 after setting up the listener in tab 2 – previously posted messages from other tabs are not recieved in tabs/windows/frames instantiated after the message has been broadcast.

Cool, cool, cool. So pretty simple – here’s the explicit flow of this experiment in case it’s not clear from the screenshot captions:

  • Open 2 tabs on the same domain – ‘https://blog.immatt.com’ in my example
  • In tab 1, create a new BroadcastChannel instance via “new BroadcastChannel(‘channel-name’);”
  • In tab 2, also create a new BroadcastChannel instance for the same channel name – e.g. “new BroadcastChannel(‘channel-name’);”
  • In tab 2, setup an event listener for the ‘onmessage’ event exposed on our BroadcastChannel instance
  • Back in tab 1, broadcast a message to all subscribers of our channel by using the ‘postMessage()’ function exposed on the ‘sender’ instance of BroadcastChannel
  • Check back in tab 2 to see our super awesome message!

Here’s the expanded console object of the recieved message for a better look at the MessageEvent payload:

Also worth mentioning, browser compatibility is pretty solid – with it having landed in most major browsers around 2015/2016 (except Edge, which was 2020).

Anywhos. There you have it… Inter-tab/window/frame/iframe communications via the BroadcastChannel Web API!

Happy Coding!
-Matt

ES/TC39 Proposals – Change Array by copy (Stage 3) – Immutable Array Operations

As a follow up to my last post, I’ve decided to dig in a little more to some of the upcoming TC39 proposals. The proposal that we’re reviewing today provides a few new ways to work with Arrays in an immutable way – a proposal titled “Change Array by copy”.

As the title “Change Array by copy” suggests, the new methods covered by the proposal provide Array functions which allow working with arrays in an immutable fashion (with the assumed intent of reducing side effects often associated with mutable objects, which are capable of being directly changed).

Included under the proposal are:

  • Array.prototype.toSorted()
  • Array.prototype.toReversed()
  • Array.prototype.toSpliced()
  • Array.prototype.with()

You can check out the collaborative view of this proposal on GitHub.

Let’s dive in!

Similarly to Array.prototype.sort(), Array.prototype.toSorted() exposes a helper function capable of sorting members of an array sequentially. Unlike to Array.protptype.sort(), Array.prototype.toSorted() returns a new array, by copy, leaving the original array untouched. To see this in action, see the following example:

Array.prototype.sort() vs Array.prototype.toSorted() – As expected, arr1 is changed, while arr2 is not.

Up next, we have Array.prototype.toReversed() – essentially, the immutable form of Array.prototype.reverse(). Just as reverse() reverses the member order of the array, so to does toReversed() – but, again, without modifying the source array being operated on/against.

Array.prototype.reverse() vs Array.prototype.toReversed() – As expected, arr1 is changed, while arr2 is not.

Last in our one-to-one function comparison is Array.prototype.toSpliced() – similar to the long standing and familiar Array.prototype.splice(), which is used to remove or replace elements within an array. As we can see in the following example, we’re capable of removing and replacing array elements with both splice() and toSpliced() – with the distinction that toSpliced() doesn’t modify the original array, and instead provides us a new, updated one via copy.

Array.prototype.splice() vs Array.prototype.toSpliced() – As expected, arr1 is changed, while arr2 is not.

And finally, we have Array.prototype.with(). Unlike the previously discussed parts of the proposal, with() doesn’t really have a similarly named existing function – though we’re all likely familiar with the logic in play. When working with an array, it’s often common practice to reassign a value within an array by assigning a new value using the target’s zero-based index – with() exposes exactly this functionality, via a function call. See the following example to see Array.prototype.with() in play:

Array index-based assignment vs Array.prototype.with() – as expected, arr1 is changed, while arr2 is not.

As you may have noticed in the above (and in case you did not) we’re once again using the core-js library to play with these new, not-yet-spec, JavaScript proposals – so check it out, and perhaps give it a spin!

Anywhos… I’m really enjoying having the opportunity to look forward and actually play with new stuff in the works for JavaScript. Yay, funemployment! That said, if you or someone you know is seeking a developer with well over a decade of full stack experience, including large enterprise engineering and architecture, please do reach out via matt@immmatt.com or ping me on Twitter @mattezell!

Happy Coding!
-Matt

ES/TC39 Proposals – Playing With Tomorrow’s JavaScript Today (core-js)

tl;dr

  • Trying to learn more
  • Ran across a JavaScript feature proposal for Array.prototype.group()
  • Peaked behind the curtain of the future of JavaScript by way of TC39
  • Learned of core-js (‘npm install core-js’)
  • Brought Array.prototype.group into my Nodejs module project via core-js (“import group from ‘core-js’;”)
  • Played with Tomorrow’s JavaScript today!

Still want to read more? Great!

I’ve been a JavaScript developer…well…for about as long as I’ve been a developer at all. That said, and admittedly a little embarrassingly, I’m far from an expert on the language – but I’m learning more everyday, including more about how little I know.

As if often the case with developers, you complete school and you quickly realize that you need a job. Armed with a bunch of CS theory, and what’s basically a beginners level of experience, you pick up new skills and tricks as you need them along the way – but rarely is there sufficient time to learn much else outside of this while maintaining balance in other parts of your life…

This is the process for years for many/most developers – you show up for work, you’re given a feature to implement, you encounter something you don’t know how to do, you do a little research, you try a few things until you find something that works, and then you move on – rarely thinking about it again. As the years go on, you keep doing that thing that you found that worked – but rarely do most of us dig in deeper to understand more or to find alternatives. Again, there’s unfortunately only so many hours in a day, week, month, year, and lifetime – and there’s a lot to living outside of punching a keyboard.

But I digress… I’m trying to be better – I’m trying to be proactive… I’m trying to dig deeper, to gain a greater understanding and to perhaps learn some alternative approaches to old problems along the way.

In my recent adventures of funemployment, I decided to play around more free-form in JavaScript… I find the MDN Web docs to be an excellent resource for JavaScript, which is where I began my journey of playing with experimental JavaScript features/proposals today. I figured “let’s get back to the basics” and take a look at the standard built-in objects, which is when I ran across the experimental proposal Array.prototype.group() – that cute little experimental beaker icon just screamed out to me.

Consider the following…

const inventory = [
  { name: "asparagus", type: "vegetables", quantity: 5 },
  { name: "bananas", type: "fruit", quantity: 0 },
  { name: "goat", type: "meat", quantity: 23 },
  { name: "cherries", type: "fruit", quantity: 5 },
  { name: "fish", type: "meat", quantity: 22 },
];

const result = inventory.group(({ type }) => type);

/* Result is:
{
  vegetables: [
    { name: 'asparagus', type: 'vegetables', quantity: 5 },
  ],
  fruit: [
    { name: "bananas", type: "fruit", quantity: 0 },
    { name: "cherries", type: "fruit", quantity: 5 }
  ],
  meat: [
    { name: "goat", type: "meat", quantity: 23 },
    { name: "fish", type: "meat", quantity: 22 }
  ]
}
*/

We have an array of JSON objects named ‘inventory’ – with standardized properties associated with each contained object. As we can see, calling array.group() passing in ‘type’ as our associative element – conceptually, we can think of this as a category. As such, we can see the resulting output of our group() call is an object comprised of our ‘type categories’, which are arrays containing the matching type objects.

I thought this was pretty cool and useful, so I wanted to try it out… Naively, I thought “Nodejs always has bleeding edge stuff – so let’s just plop in our playground code…”.

D:\w\nodejs\csv.js:45
jsonArr.group((country => country));
        ^

TypeError: jsonArr.group is not a function
    at Object.<anonymous> (D:\w\nodejs\csv.js:45:9)
    at Module._compile (node:internal/modules/cjs/loader:1159:14)
    at Module._extensions..js (node:internal/modules/cjs/loader:1213:10)
    at Module.load (node:internal/modules/cjs/loader:1037:32)
    at Module._load (node:internal/modules/cjs/loader:878:12)
    at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:81:12)
    at node:internal/main/run_main_module:23:47

Node.js v18.12.1

Whelp… That’s not going to get it – Array.prototype.group() doesn’t exist… Why? Because it’s not part of JavaScript today – it’s a proposal for the future… Time to grow that brain! How might one, if so inclined, learn about these experimental JavaScript features and proposals?

Enter TC39 – “a group of JavaScript developers, implementers, academics, and more, collaborating with the community to maintain and evolve the definition of JavaScript” (from the official website). Cool. So, let’s take a peak at the stage 3 draft proposal for Array Grouping on Github.

It’s from the proposal that a whole new world of experimentation was shown to me… Down at the bottom of the proposal, I notice a link to an external polyfill library – ‘core-js‘… Wait… Does this mean I get to play with the future of JavaScript today?! Yup!

This is all new to me, so I figured I should just start throwing stuff at the wall to see what sticks… Up first, let’s just install…

npm install --save core-js

Now let’s figure out how to use this bad boy… Since I have my Nodejs project setup as a module, I figure I will just try to import group. Note: I’m not saying this is the best way to go about this, or anything of the sort – just sharing my process of discovery in case there’s interest.

import group from 'core-js';

So far, so good.

Now, consider my project… It’s a basic utility script – imports a CSV, and then coerces the CSV data into JSON array containing objects that represent said data…

Here’s my CSV contents:

name;role;country
Sarene;Help Desk Operator;Thailand
Olvan;Nurse Practicioner;China
Janos;Cost Accountant;China
Dolph;Assistant Manager;China
Ariela;Database Administrator I;Azerbaijan
Lane;Environmental Tech;Indonesia
Griselda;Senior Quality Engineer;Portugal
Manda;Physical Therapy Assistant;Brazil
Leslie;Information Systems Manager;Japan
Aleen;Cost Accountant;Canada

Here’s my main coercion function and call to it (assume we’ve already read in and cleaned up our CSV):

function csvTxtToJsonArr(csvTxt, delimiter = ',') {
    const headers = csvTxt.slice(0, csvTxt.indexOf('\n')).split(delimiter);
    const rowStrs = csvTxt.slice(csvTxt.indexOf('\n') + 1).split('\n');
    const jsonArr = rowStrs.map((currRowStr) => {
        const rowVals = currRowStr.split(delimiter);
        const mapObj = headers.reduce((newObj, currHeader, idx) => {
            newObj[currHeader] = rowVals[idx];
            return newObj;
        }, {});
        return mapObj;
    });
    return jsonArr
}

const jsonArr = csvTxtToJsonArr(csvContents, ';');

Which results in a jsonArr that looks like this:

[
  { name: 'Sarene', role: 'Help Desk Operator', country: 'Thailand' },
  { name: 'Olvan', role: 'Nurse Practicioner', country: 'China' },
  { name: 'Janos', role: 'Cost Accountant', country: 'China' },
  { name: 'Dolph', role: 'Assistant Manager', country: 'China' },
  {
    name: 'Ariela',
    role: 'Database Administrator I',
    country: 'Azerbaijan'
  },
  { name: 'Lane', role: 'Environmental Tech', country: 'Indonesia' },
  {
    name: 'Griselda',
    role: 'Senior Quality Engineer',
    country: 'Portugal'
  },
  {
    name: 'Manda',
    role: 'Physical Therapy Assistant',
    country: 'Brazil'
  },
  {
    name: 'Leslie',
    role: 'Information Systems Manager',
    country: 'Japan'
  },
  { name: 'Aleen', role: 'Cost Accountant', country: 'Canada' }
]

Cool… I guess… I mean, just basic, boring CSV play… But group – yeah, Array.prototype.group() seems like it might be fun here… So, let’s try it out using country as a grouping element:

jsonArr.group(({country}) => country);

Which gives us the following results:

{
  Thailand: [
    { name: 'Sarene', role: 'Help Desk Operator', country: 'Thailand' }
  ],
  China: [
    { name: 'Olvan', role: 'Nurse Practicioner', country: 'China' },
    { name: 'Janos', role: 'Cost Accountant', country: 'China' },
    { name: 'Dolph', role: 'Assistant Manager', country: 'China' }
  ],
  Azerbaijan: [
    {
      name: 'Ariela',
      role: 'Database Administrator I',
      country: 'Azerbaijan'
    }
  ],
  Indonesia: [
    { name: 'Lane', role: 'Environmental Tech', country: 'Indonesia' }
  ],
  Portugal: [
    {
      name: 'Griselda',
      role: 'Senior Quality Engineer',
      country: 'Portugal'
    }
  ],
  Brazil: [
    {
      name: 'Manda',
      role: 'Physical Therapy Assistant',
      country: 'Brazil'
    }
  ],
  Japan: [
    {
      name: 'Leslie',
      role: 'Information Systems Manager',
      country: 'Japan'
    }
  ],
  Canada: [ { name: 'Aleen', role: 'Cost Accountant', country: 'Canada' } ]
}

Cool! Right?!

Will Array.prototype.group() become standard spec? I can’t say, but it looks promising… But I suppose what I wanted to share more than this cool new group proposal was how you and I can be a little more hands on with what’s potentially to come to the future of JavaScript by way of the handy-dandy core-js library!

I feel that it’s worth mentioning another interesting and useful resource I ran across while playing this morning… The unofficial ES Proposals site, which is a labor of love from Apple Software Engineer Saad Quadri. Check it out!

Anywhos… Happy coding!
-Matt

Standalone Components in Angular 15

Up next in the Funemployment series, I’m taking a look at Standalone Components – specifically, Standalone Components in Angular 15.

Since I follow the official Angular Twitter account, I recently saw a link to this tutorial shared and so I figured it would be a good place to start: https://codelabs.developers.google.com/angular-standalone-components

Standalone Components and Chatbots?! Perfect!

One of the first things one might notice is that the tutorial targets Angular 14 – but since I’m really wanting to get a jump on Angular 15, I figured I’d forge ahead and try to tackle whatever issues may arise as they arise.

My environment

Since the tutorial largely just works in Angular 15, I’m not going to reproduce it step-by-step here – instead, I’m just going to highlight some of the issues I encountered when following along verbatim.

tl;dr:
1) environments/environment is no longer generated by default: https://github.com/angular/angular-cli/commit/283b564d1de985f0af8c2fcb6192801a90baacda

2) ngOnInit and default constructor no longer generated by default: https://github.com/angular/angular-cli/commit/301b5669a724261d53444d5172334966903078c0

So, yeah… The tutorial mostly just translates to Angular 15, and most experienced Angular developers will be able to quickly workaround any issues that may arise from working through this Angular 14 tutorial in Angular 15… But if you’re curious about my journey, please feel free to continue on…

Hiccup #1 – in the ‘Create a new application’ section of the tutorial, we’re shown how to strip down our application in order to remove the ngModule (basically a cornerstone of ‘standalone components’) – and as part of those instructions, we’re shown how to enable production mode by leveraging the long familiar ./environments/environment.ts file… Okay… But I’m getting an error indicating this long standing file isn’t present in my project… odd… that fella is always hanging out there…

Can’t reference what doesn’t exist…

Since I can’t ever seem to just do what I set out to do, I must now satisfy my curiosity concerning why this file, which was previously generated as part of the new project, no longer exists… After a bit of poking around in the Angular CLI github repo, I see the following: https://github.com/angular/angular-cli/commit/283b564d1de985f0af8c2fcb6192801a90baacda

feat(@schematics/angular): remove environment files in new applications

This commit removes the usage of environment files and `fileReplacements` in new application projects. Previously, the environment files was used to distinguish between a prod build to invoke `enableProdMode`. The `enableProdMode` however needed only for the case of JIT mode in production mode, which is a rare case as JIT mode is recommanded to be used in production.

In the CLI, calling `enableProdMode` is not needed as `ngDevMode` it's set using the minifier.

Whelp… That settles that… Moving on…

Since we no longer have this environment.ts file out of the box with new apps, it seems logical to assume we can skip some steps – basically, we just need to ensure we bootstrap our now-standalone AppComponent.

main.ts for Angular 15 Standalone Component

Skipping ahead to the next section, “Display the photos”, things seem straight forward enough… It’s worth noting that there seems to be a bit of a typo in the HTML, as the closing ‘article’ tag isn’t properly formed – no worries, as we just need to add an ‘>’ to make it valid.

Screenshot of tutorial showing ‘</article’ rather than ‘</article>’

Upon saving and running our updated project, we will see that while things are technically working, they aren’t working great because we’re missing some assets – namely, the images…

Hmm.. That doesn’t look very nice…

This makes sense, as image assets aren’t going to be generated by ‘ng new’ and are merely artifacts for this demo project… For simplicity, I’m just going to download these assets from the github project for this demo app: https://github.com/angular/codelabs/tree/standalone-components/src/assets

This looks better…

Continuing on to ‘Add a new standalone component’, everything in the original tutorial translates perfectly to Angular 15 and we can see our lazy loaded component as expected when we click the ‘Find out more about these guys!’ button as instructed:

Kewwwwl…

Another small note to make… Once you get to the ‘Add event handling to the form‘ section, step 4 of the ‘Add the send message form model to the component’ subsection instructs us to remove the default constructor:

Whelp… We don’t have a constructor in our newly created Angular 15 standalone component… Not particularly important, as it was empty and not really doing anything – but why doesn’t it exist?! MY ADHD WILL NOT ALLOW ME TO CONTINUE ON WITH WHAT I SET OUT TO DO UNTIL I KNOW… After a bit more poking around the Angular CLI repo, I see the following indicating that ngOnInit and the default constructor have been removed in Angular 15, as apparently users of these features generally prefer to simply add them manually: https://github.com/angular/angular-cli/commit/301b5669a724261d53444d5172334966903078c0

Moving on…

Reactive form up and running…

The next section, ‘Add a chat dialog’, just works. Unrelated note: I really liked this section… Having never played with a virtual agent / chat system, I learned something new, which is always fun! And that’s pretty much it – standalone components in Angular 15…

NEAT-O!

At this point, I’m going to stick a fork in this write up and consider it done. The remainder of the Angular Standalone Component tutorial details deploying the project to Google App Engine with Express.js – which I feel is a bit out of scope for this write up, which was to detail the hang ups I encountered working through this Angular 14 tutorial in Angular 15 (which were very minimal – YAY!).

Happy coding!
-Matt

Exploring Denoland – Home of the Deno JavaScript Runtime

With some of my newly found ‘free time’ while Funemployed, I really wanted to start playing around with some of the newer projects that I’ve simply not had the time or energy for.

At the top of this list has been Deno, the new-ish JavaScript, TypeScript and WebAssembly runtime based on V8, co-created by Ryan Dahl (Node.js creator).

Why Deno? Well, for starters I love new and shiny things. But also, I have a special place in my heart for JavaScript runtimes – having been an early adopter of Node.js, at a time when serverside JavaScript was a controversial topic in the dev world (Thanks, Whiteboard IT! It’s because of my time with this company that I was exposed to a lot of really cool things, including Node.js and CouchDB/NoSQL). “How early?”, one might ask… Early enough that I’m listed as contributor #306 in the Node.js AUTHORS file – as a result of contributing a slash to the first official Windows installer.

I began my journey with Deno as I normally do – essentially iterating on some basic Hello World types of projects to get familiar with the dev flow, tooling and capabilities. Pretty quickly, I encountered something in my codebase that confused me – a warning notifying me that the deno window typings didn’t have everything supported by window exposed. Surprisingly, it wasn’t just me randomly plugging in code that landed me here – it was in following along with the Deno lifecycle docs that brought me to this error.

“Element implicitly has an ‘any’ type because type ‘typeof globalThis’ has no index signature.deno-ts(7017)”

At this point in my journey, not only was I getting the warning in VSCode, but I was also unable to get beforeunload or onbeforeunload to fire… Hmm… So off to Google I go – which lead me to this thread discussing a related matter.

In digging a bit deeper, I came to realize that beforeunload/onbeforeunload weren’t firing due to my Deno version – with support for these not being added to window in Deno until v1.24.0 (and me currently running v1.23.0). “Alright”, I thought – “I just need to upgrade and this will all be solved.”

Sure enough, once on 1.24.0 I was able to play with beforeunload/onbeforeunload – but the “Element implicitly has an ‘any’ type because type ‘typeof globalThis’ has no index signature.deno-ts(7017)” warning remained…

Having wished I had more time to contribute to open source over the last several years, I saw this as an opportunity to make my wish a reality – and so I did what any developer would do, created a fork and started to work on a new branch in the hopes I could become a Deno contributor!

As most experienced devs know, stepping into a large and complex codebase isn’t always the easiest thing to do – especially if you’ve never worked with some of the core tooling. With this being my first real exposure to Rust, I was completely unfamiliar with Cargo and how to go about building and testing my changes in an efficient manner… Fortunately, after a handful of web searches, I learned the basics of working in Rust and with Cargo, how to run specific tests by name, and I was off to the races to become a real life Deno contributor.

My commit is simple enough – really just a few lines to add beforeunload and onbeforeunload to the type definition for Deno window, and some updated integration tests to ensure everything is working as expected – you can see the pull request / merge here. While a small contribution by pretty much all standards of measurement, it felt good to see an issue, fix the issue and contribute that fix back to the community – and I hope to be doing more of this in the near future.

So, what’s next? Well, I guess I can actually begin with what I set out to do – learn Deno (rather than Deno’s codebase) and start playing with Fresh! 😁

Happy Coding!
-Matt

Site Migration Update

Good day, fine reader.

Just a quick post – if for not other reason than I’ve not made a post in a while. As you may know if you follow me on social media elsewhere, I was laid off from my job of ~12yrs middle of September – so, as of today, I’ve been FUNemployed 25 days (It’s okay. I was offered the option to relocate, but that unfortunately was not really possible as my wife and I are somewhat rooted where we are at). One random observation about being unemployed – non-work time passes by twice as quickly as work time.

Anywhos… One of my goals for my newly found free time was to move all of my personal sites to a new VPS instance since I’d been on the previous underpowered instance for about 6 years. I’m happy to say that I was able to get the new server set up, and all sites+content migrated with next to no downtime (aside from intentional reboots for OS/package updates). If you see something not quite right, please reach out and let me know – but in my testing and spot checking, nothing significant is standing out.

So, aside from a new server setup, what’s to come? I can’t really say. I plan on spending some of my free time picking up some new skills (Nunchaku?), so tune back in from time to time to see what I’m doing on that front if you’re interested.

I am currently seeking a new gig, though not particularly aggressively – so if you know of something that would be a good fit for an experienced full stack developer with a wealth of Angular experience, please don’t hesitate to reach out to me. In the meantime, I’m going to do a bit of learning, experimenting, playing and honey-dos 🙂

Take care!
-Matt

Bootstrap 5 – Using components without jQuery in Angular

I recently found myself on yet another Angular + Bootstrap project. Usually, when spinning up a new Angular Bootstrap project, this means npm installing a library like @ng-bootstrap, but this time we wanted to keep things as vanilla as possible.

“How to use Bootstrap in an Angular application without jQuery?”

Bootstrap

Since we’re in Angular, and since it’s generally considered a bad idea to use jQuery in an Angular project (though it is most certainly possible), we were forced to think about how to best use Bootstrap “from scratch”.

Up first – get a major pre-req out of the way… It’s been a while, so I guess I should ensure I’m on latest LTS😲

Install Node and NPM

For this project, we’re also going with the latest and greatest from Angular, so…

Ensuring the global @angular/cli is up to date – using v14 here…

Now lets get a clean Angular workspace spun up…

> ng new bootstrap-5-demo

For simplicity, we will start slow – simply displaying a Modal from the AppComponent in the presentation of a Confirmation Dialog.

I’m opting to go with a Modal because it’s a frequently desired component of Bootstrap, and I believe that it allows us to demonstrate a number of angles with regards to using a Bootstrap Modal ‘natively’ from Angular (without jQuery).

Normally, I’d opt to go with self-hosting for my libraries, but since this crash course is more focused on how to use Bootstrap natively in an Angular application rather than productionizing the solution, I’m just going to use a CDN from the Bootstrap getting started docs to get things wired up.

Setting up Bootstrap and the Popper dependency in index.html

To make things a bit easier on ourselves, we’re also going to install the Bootstrap types package from the Definitely Typed project as a developer dependency in our project.

npm install @types/bootstrap --save-dev

Now that we have the basics set up, let’s start in our AppComponent by importing Modal from bootstrap (via the @types/bootstrap package installed in the previous step):

Importing Modal with help from the @types library

To get our Modal setup, we’re going to utilize the AfterViewInit lifecycle hook to wire up our event listeners so that we can respond to our Modal instance:

Getting AfterViewInit in place

As a sanity check, let’s go ahead and wire up the Modal as simply as we can – triggering the modal show with a button click:

Basic Modal scaffolding and a button passing in the HTMLElement via the ‘confirmationDialog’ template variable to the openModal method.
Basic openModal implementation – instantiating a new Modal and assigning it to our private ‘confirmDialog’ property.

As you can see in the above code, we’re able to simply create a Modal instance in TypeScript-land thanks to our @types/bootstrap library -then we can simply call ‘show()’ to open our new Modal:

WOW! SUCH MODAL!

Okay. That’s all well and good, but unless we’re just wanting to show some static message, this Modal is pretty lackluster.

Let’s go back to our AfterViewInit lifecycle and set some things up… But what exactly? Perhaps we should go take a quick, cursory glance at the Modal docs

Modal Events

Alright… So we’ve got a handful of events we can respond to… For the purposes of this example, we’re going to focus in on ‘hide’ and ‘show’.

In addition to wiring up event listeners for ‘show’ and ‘hide’, we’re also going to change our implementation a little bit – rather than passing in the element from our template, we’re going to access the element via the @ViewChild property decorator. And lastly, we will log our events to the console:

Getting Modal from template via @ViewChild decorator rather than passing in to click handler

Some points to note in the above code… We’ve introduced the Angular property decorator @ViewChild(…) – this has enabled us to stop passing in the element from the template via the template variable as we were originally doing. We’ve also added some code to our ngAfterViewInit lifecycle method, which is responsible for listening to the ‘show’ and ‘hide’ events – simply logging the ‘type’ property associated with the Event via the processDialogEvent() method.

Let’s check it out:

WOW! SUCH MODAL!…still…

Whelp… Still pretty lackluster… One could be forgiven for mistakenly concluding we’re just showing the same demo as earlier – but we know they’d be wrong because this is a completely different monster altogether (since we’re using ViewChild and listening to/logging events now)!

Alright… Let’s get this thing done… Right now, we just have a message modal – but we need a confirmation dialog… So we will spruce things up a bit with some formatting/structure and we will add a couple of user prompts – something for them to confirm (or not).

Hamming up the Modal a bit

And here’s how it looks in action:

New Modal look

Now it’s starting to look more like a proper confirmation dialog!

Something I’d like to point out at this point in the process. Remember our event listeners? Perhaps you noticed we added a new ‘X’ close button element, and that this element has an interesting ‘data-bs-dismiss’ now present. Bootstrap supports interaction via data attributes, and this attribute demonstrates how to close this modal instance using one such data attribute – read more here in the docs. Oh, yeah – and the event listeners? Still listening – even when we’re closing via the data attribute:

Still listening for events from the Modal even through we’re explicitly handling clicks

But we still need to do something about those button clicks… And as cool as being able to close the modal via the data attribute is, we don’t really know if the close was by clicking outside of the dialog or if it was intentional via the ‘X’ – and I’d think the ‘X’ could commonly be interpreted as “No”, as where closing via clicking outside of the dialog could be “I’m declining to answer right now”, and that perhaps we want to handle those two interactions differently.

First up, let’s add a new method to assess if the user likes to rock or not – ‘userRocks’:

Getting user response

Next, let’s wire this up on the template to the ‘X’ close button and the other choice buttons – passing ‘true’ when they do and ‘false’ when they don’t (I have a feeling those ‘false’ ones rarely get used, and only by people you probably wouldn’t enjoy writing apps for):

Wire up user actionable controls so that the application can react

And let’s see it in action:

Updated view of Modal

Cool-cool-cool. Looking more and more like a proper Confirm Dialog by the minute! But we have a bit of an issue… We know if the user rocks or not – but most users would expect the dialog to do something other than log to the console, which they wouldn’t even be aware of unless they’re a super 1337 hacker looking at the console while trying to find their exploit.

Let’s start simply by having the modal hide after the user has provided an answer… Easy enough – let’s just modify our userRocks method to call ‘hide()’ on our Modal instance:

Closing the modal after user input

Now we’re cooking! Let’s put this baby to bed! At this point in time, we have a functional confirmation dialog that allows the user to let us know if they like to rock or not – all that’s left is to do something based on the user’s willingness to rock.

For the sake of brevity (🤣🤣🤣 Never been a strong suit of mine), we’re just going to introduce a few lines of code to our userRocks method and some supporting code to our AppComponent implementation. Namely, we’re going to add another Modal (why not?! This is a Modal example, afterall!) as well as another ViewChild that makes accessing an element within the Modal a bit easier so that we can mess with it in our userRocks() method based on the user’s input.

Here’s our updated component TS:

The whole shebang!

And here’s our updated template:

The rest…of the whole…shebang…

And here’s how it looks running:

A done-ish Modal

We like to rock:

Rocking out…

We have sensitive ears and “refined” musical tastes:

Not rocking out…

There you have it – how to use Bootstrap 5 from Angular 14 ‘natively’.

While we really only covered the Modal here, many/most of the approach we’ve reviewed here translates to other Bootstrap Components.

I hope that you’ve found this helpful and perhaps even a little entertaining. If you would like to check out the full source for this project, feel free to check out the project here: https://github.com/mattezell/bootstrap-5-demo

Tootles!
-Matt

The Day Facebook, Instagram, and Co Died – The Rise of Twitter

Okay, okay… I’m sure Facebook, Instagram, WhatsApp, and Co won’t be going anywhere anytime soon – but today has been interesting.

Unless you live under a rock and/or actually followed through with your declaration to leave Facebook “for totes real this time”, you’re likely very aware of today’s outage of some very popular services.

With a mixture of cries, sighs, and giggles, the world has been collectively forced to come to terms with what a day in their life without Facebook/Instagram would look like.

To me, one of the highlights today has been the brand clowing taking place on Twitter (unquestionably the dominant social media platform today), which has included Facebook brands, such as Instagram and WhatsApp, playing along themselves.

I won’t go too deep into the technical, as there are people far more qualified than I who have already done so (here’s one from ArsTechnica). But basically, there are services that monitor and share routes that are used to make devices/services communicate with other devices/services on the Internet (RGP – Border Gateway Protocol) – and a configuration update was pushed(reportedly by Facebook themselves) that deleted the routes that told the rest of the Internet how to access the Facebook-owned stuff.

To add insult to injury, reportedly the Facebook staff tasked with cleaning up this mess have been locked out of the physical locations that they require access to as a result of this screwup 🤣 Making matters worse, both Facebook/Instagram users, coupled with client applications (such as the Facebook and Instagram apps installed on Phones that automatically poll for updates), are hammering all of the DNS services that play a role in getting users from point A to point B, bringing these vital services to their knees. We’re now hours into the outage, with no end in sight – it’s almost as if the Internet is a house of cards, just waiting to collapse (Narrator: “it is”).

Anywho… Here are some highlights:

« Older posts

© 2024 blog.immatt

Theme by Anders NorenUp ↑