Angular Signals Code Lab Drag & Drop Letters

After looking over some purely decorative CSS in the Angular Signals code lab sample application, I dug around elsewhere in the source code for interesting things to learn. The next item that caught my attention was the “keyboard” where we drag-and-dropped letters to create our decoder. How was this done?

Inside the HTML template code, I found an attribute cdkDropListGroup on the keyboard container. A web search pointed me to the Angular CDK (Control Development Kit.) Angular CDK is a library that packages many common web app behaviors we can use in our own custom controls, one of them being drag-and-drop as used in the Angular Signals sample app. The CDK is apparently under the umbrella of Angular Material, which has a set of fully implemented app controls implemented to the Material Design specification. Many of them use the CDK for their own implementation.

Drag-and-drop behavior in Angular CDK is very flexible and has many options for configuring behavior. Such flexibility and options unfortunately also meant it’s easy for a beginner to get lost. I’m thankful I have the Angular Signals code lab cipher app. It lets me look over a very specific simple use of CDK drag-and-drop.

Here’s an excerpt of the HTML template for the cipher keyboard in file cipher.ts, stripped of everything unrelated to drag-and-drop.

<div class="cipher-wrapper" cdkDropListGroup>
    <div class="key-container">
      <letter-key
        *ngFor="let l of this.cipher.alphabet"
        cdkDropList
        cdkDropListSortingDisabled
        [cdkDropListData]="l"/>
    </div>
    <div class="guess-container"
      cdkDropList
      cdkDropListSortingDisabled>
      <letter-guess
        *ngFor="let l of this.cipher.unsolvedAlphabet()"
        cdkDrag
        [cdkDragData]="l"
        (cdkDragDropped)="drop($event)">
        <div class="placeholder" *cdkDragPlaceholder></div>
      </letter-guess>
    </div>
  </div>

It has two containers, one for a list of custom control letter-key and and another for a list of letter-guess. Drag-and-drop is all encapsulated here in cipher.ts, there’s nothing in either of those two controls concerning drag-and-drop.

Uniting these two containers is a div with cdkDropListGroup which associates all child cdkDropList elements together. This allows us to drag individual letter-guess (tagged with cdkDrag) from one cdkDropList onto the sibling cdkDropList of letter-key. These properties are enough to let Angular CDK know how to respond to pointer input events to manipulate these elements. All the app has to do is register a cdkDragDropped listener for when a cdkDrag element is dropped into a cdkDropList.

I poked around the code looking for how a letter-guess sits in the key-container after it has been dropped into the right location. The answer is: it doesn’t, that’s just an illusion. When a letter is dropped into the correct location, it is removed from the list returned by this.cipher.unsolvedalphabet(). Meaning that particular letter-guess I had been dragging disappears. The letter-key I had dragged it onto, however, will pick up a new CSS class and change its appearance to look as if the letter-guess stayed in that location.

I had to spend some time flipping between looking at source code and looking at CDK drag-and-drop API documentation. But once I made that time investment, I could understand how this app utilized the library. Once understood, I’m impressed at how little work is required in an Angular app to pick up very complex behavior from Angular CDK.

I look forward to leveraging this capability in my own projects in the future. Before that, though, I can try using Angular signals in my Compass practice app.

Angular Signals Code Lab Decorative CSS

I want to understand the Angular Signals code lab project beyond what was set up for signals practice. While learning why layout for <body> looks funny, I stumbled across CSS quirks mode which I hadn’t known before. And since I’m already in a CSS mindset, I stayed on topic to understand a few places where the sample app used CSS to create aesthetic visuals.

The first item I wanted to understand was a large list of <div> in index.html, taking up more than half of the lines in the file. I originally thought it had something to do with the alphabet cipher keyboard, but it was actually implementation of the fake speaker grill. CSS class .sound-grid is a grid of 8 columns filled with a <div> styled to be a small circle. There were 48 of them to create 6 rows in those 8 columns. Some of these circles are dark representing holes, some light representing… something else, and four corner circles were transparent to de-emphasize the rectangular nature of a grid.

That was kind of neat. And the next item I wanted to understand was the green screen display resembling a monochrome LED like an old Game Boy. I was curious how the graph paper grid was implemented, and the answer is a CSS linear gradient (class .message::before) given parameters to be very not smooth in the gradient transition in order to create a grid. I was mildly confused looking at the gradient and text styles, as they are all working in grayscale. The answer is another piece of CSS (.message::after) that gave a green tint over the entire screen area plus a bit of blur for good effect.

While these are nifty creations, I am curious why CSS was used here. Both the fake speaker and screen grid feel like vector graphic tasks, which I had thought was the domain of either SVG (if via markup) or canvas (if via code). What’s the advantage of using CSS instead? Sure, it worked in this case, but it feels like using the wrong tool for the job. I hope to eventually learn reasons beyond “because we can”. For now, I turn my attention to other functional bits of this sample application.

Angular Signals Code Lab CSS Requires “No-Quirks” Mode

The sample application for “Getting Started with Angular Signals” is designed to run on the StackBlitz cloud-based development environment. Getting it up and running properly on my local machine (after solving installation and compilation errors) took more effort than I had expected. I wouldn’t call the experience enjoyable, but it was certainly an educational trip through all the infrastructure underlying an Angular app. Now I have all the functional components up and running, I turn my attention to the visual appearance of the app. The layout only seems to work for certain window sizes and not others. I saw a chance for me to practice my CSS layout debugging skills, but what it also taught me was “HTML quirks mode”.

The sample app was styled to resemble a small hand-held device in the vein of the original Nintendo Game Boy: a monochrome screen up top and a keyboard on the bottom. (On second thought, maybe it’s supposed to be a Blackberry.) This app didn’t have audio effects, but there’s a little fake speaker grill in the lower right for a visual finish. The green “enclosure” of this handheld device is the <body> tag of the page, styled with border-radius for its rounded corners and box-shadow to hint at 3D shape.

When things go wrong, the screen and keyboard spills over the right edge of the body. Each of which had CSS specifying a height of 47% and an almost-square aspect-ratio of 10/9. The width, then, would be a function of those two values. The fact that they become too wide and spill over the right edge means they have “too much” height for the specified aspect ratio.

Working my way up the component tree, I found the source of “too much” height was the body tag, which has CSS specifying a width (clamped within a range) and an aspect-ratio of 10/17. The height, then, should be a function of those two values. When things go wrong, the width seems to be clamped to maximum of specified range as expected, but the body is too tall. Something has taken precedence over aspect-ratio:10/17 but that’s where I got stuck: I couldn’t figure out what the CSS layout system had decided was more important than maintaining aspect ratio.

After failing to find an explanation on my own, I turned to the StackBlitz example which worked correctly. Since I’ve learned the online StackBlitz example isn’t exactly the same as the GitHub repository, the first thing I did is to compare CSS. They seemed to match minus the syntax errors I had to fix locally, so that’s not it. I had a hypothesis that StackBlitz has something in their IDE page hierarchy and that’s why it worked in the preview pane. But clicking “Open in new tab” to run the app independent of the rest of StackBlitz IDE HTML still looks fine. Inspecting the object tree and associated stylesheets side-by-side, I saw that my local copy seems to have duplicated styles. But since that just meant one copy completely overrides the other identical copy, it wouldn’t be the explanation.

The next difference I noticed between StackBlitz and local copy is the HTML document type declaration at the top of index.html.

<!DOCTYPE html>

This is absent from project source code, but StackBlitz added it to the root when it opened the app in a new tab. I doubted it had anything to do with my problem because it isn’t a CSS declaration. But in the interest of eliminating differences between them, I added <!DOCTYPE html> to the top of my index.html.

I was amazed to find that was the key. CSS layout now respects aspect-ratio and constrains height of the body, which kept screen and keyboard from spilling over. But… why does the HTML document type declaration affect CSS behavior? A web search eventually led me to the answer: backwards compatibility or “Quirks Mode”. By default, browsers emulate behavior of older browsers. What are those non-standards-compliant behaviors? That is a deep dark rabbit hole I intend to avoid as much as I can. But it’s clear one or more quirks affected aspect-ratio used in this sample app. Having the HTML document type declaration at the top of my HTML activates the “no-quirks” mode that intends to strictly adhere to modern HTML and CSS standards, and now layout works as intended.

The moral of today’s story: Remember to put <!DOCTYPE html> at the top of my index.html for every web app project. If things go wrong, at least the mistake is likely my own fault. Without the tag, there are intentional weirdness because some old browser got things wrong years ago and I don’t want that to mess me up. (Again.)

Right now, I have a hard enough time getting CSS to do my bidding for normal things. Long term, I want to become familiar enough with CSS to make it do not just functional but also fun decorative things.


My code changes are made in my fork of the code lab repository in branch signals-get-started.

Running “Getting Started with Angular Signals” Code Lab Locally

After fixing local Angular build errors for the Getting Started with Angular Signals code lab sample application, I saw it was up and running but not running correctly. Given the fact I had to fix installation errors for this sample before fixing the build errors, seeing runtime errors were not a surprise.

Of course, some of the missing functionality is intentional, because this is a hands-on code lab where we fill in a few intentional gaps to see Angular signals in action. I’ve done the exercise once before on StackBlitz so I knew what to expect. The signal() and computed() exercises went smoothly, but the effect() had no effect. Every time solvedMessage signal changed, our effect() was supposed to compare it against superSecretMessage. And if they matched, launch a little confetti effect. I saw the completion confetti on StackBlitz, and nothing on my local version. Time to start debugging.

There were no error messages, so I started by adding a few console.log() to see what is and isn’t happening. It led me to the conclusion that the code inside my effect() call was not getting executed. This is a bit of a pickle. Standard debugging procedure is to attach a breakpoint to an interesting piece of code and see what happens when it is called. But in this case, my problem is that the code was not getting called. D’oh! A breakpoint on code inside effect() would be useless because it is never hit. In these situations, the next thing to try is setting a breakpoint somewhere upstream. Ideally in the bit of logic that decided whether to call my code or not. Unfortunately, here it means setting a breakpoint somewhere inside Angular’s implementation of effect() and I don’t know where that is or where to even start looking. I barely understand code in an Angular application, digging into Angular framework internals is beyond my current skill level.

At a loss on how to find my answer in code, I decided to revisit the documentation. Specifically, re-reading Angular Signals developer guide. Since it’s a new feature in developer preview, it’s still relatively sparse document and not something overwhelmingly long. I hoped to find an answer here and the most promising information is about Injection context. It explained effect() requires something called an injection context and gave a few examples of how to make sure an effect() has what it needs. The document didn’t say what happens if an effect() did not have an injection context so I have no idea if my problem match the expected symptoms. Still, it was worth a shot.

For the code lab, we were instructed to put our effect() inside the ngOnInit() function. I don’t know if ngOnInit() has an injection context, but it isn’t in any of the examples listed in documentation. I followed the first example of putting my effect() in the constructor. Once that code was moved, my console.log() messages inside started sending data to the developer console and, when I solved the cipher, I see a pop of confetti. Success!

I’m glad that worked, because it was a stab in the dark and I’m not sure what I would try next if it had not worked. So how did the StackBlitz example’s effect() throw up confetti upon completion if ngOnInit() had no injection context? Comparing code I see a difference: in the StackBlitz project, the prompt “// TODO(3): Add your first effect()” was in the constructor and not ngOnInit() as per both the GitHub repository and code lab instruction text. I had wrongly thought StackBlitz cloned from the same GitHub repository. And now that I know the code is different, it helps explain “How the heck did this even work on StackBlitz” mystery.

I’m glad I got the code lab application up and running correctly locally on my own computer, but there’s something weird with its CSS layout I need to investigate.


My code changes are made in my fork of the code lab repository in branch signals-get-started.

Compiling “Getting Started with Angular Signals” Code Lab Locally

It was easy to follow “Getting Started with Angular Signals” tasks with the online StackBlitz development environment, but as a practice exercise I wanted to get it up and running on my own computer. This turned out to be more challenging than expected, with several problems I had to fix before I could install dependencies on my computer with “npm install“.

Successful installation of project dependencies moved me to the next part of the challenge: Angular project issues. Running “ng serve” resulted in the following error:

Error: Schema validation failed with the following errors:
  Data path "" must have required property 'tsConfig'.

Unfortunately, there was no filename associated with this error message, so it took a lot of wrong turns before I found the answer. I compared files between my Compass project and this demo project and eventually figured out this was referring to the "build"/"options" section in angular.json. In my Compass project, I had a “tsConfig” entry pointing to a file tsconfig.app.json. This code lab exercise project’s angular.json is missing that entry and missing the file tsconfig.app.json. I copied both from my Compass project to reach the next set of errors:

Error: src/cipher/cipher.ts:1:21 - error TS2305: Module '"@angular/core"' has no exported member 'effect'.

1 import { Component, effect, OnInit } from '@angular/core';

This is just the first of several errors, all complaining that the newfangled Angular signal mechanisms signal, computed, and effect were missing from @angular/core. Well, of course they wouldn’t be. The packages.json file specified Angular 15, and signals were one of the new touted features of Angular 16. Running “ng update” I was informed of the following:

Using package manager: npm
Collecting installed dependencies...
Found 25 dependencies.
    We analyzed your package.json, there are some packages to update:
    
      Name                               Version                  Command to update
     --------------------------------------------------------------------------------
      @angular/cdk                       15.2.9 -> 16.0.1         ng update @angular/cdk
      @angular/cli                       15.2.8 -> 16.0.2         ng update @angular/cli
      @angular/core                      15.2.9 -> 16.0.3         ng update @angular/core
      @angular/material                  15.2.9 -> 16.0.1         ng update @angular/material

So I ran “ng update @angular/cdk @angular/cli @angular/core @angular/material“. It was mostly successful but there was one error:

Migration failed: Tsconfig cannot not be read: /src/tsconfig.spec.json
  See "/tmp/ng-QdC8Cc/angular-errors.log" for further details.

Judging by the “spec.json” suffix this has something to do with Angular testing and I will choose to ignore that error. Now when I run “ng serve” I no longer get complaints about missing Angular signal mechanisms. I get syntax errors instead:

./src/cipher/guess/letter-guess.ts-2.css?ngResource!=!./node_modules/@ngtools/webpack/src/loaders/inline-resource.js!./src/cipher/guess/letter-guess.ts - Error: Module build failed (from ./node_modules/postcss-loader/dist/cjs.js):
SyntaxError

(17:5) /workspaces/codelabs/src/cipher/guess/letter-guess.ts Unclosed block

  15 |     }
  16 | 
> 17 |     p {
     |     ^
  18 |       font-size: clamp(16px, 3vw, 20px);
  19 |       margin: 0;


./src/secret-message/secret-message.ts-5.css?ngResource!=!./node_modules/@ngtools/webpack/src/loaders/inline-resource.js!./src/secret-message/secret-message.ts - Error: Module build failed (from ./node_modules/postcss-loader/dist/cjs.js):
SyntaxError

(87:10) /workspaces/codelabs/src/secret-message/secret-message.ts Unknown word

  85 |       font-size: clamp(10px, 3vw, 20px);
  86 | 
> 87 |       // color: #c0e0c7;
     |          ^
  88 |       text-shadow: -1px -1px 1.2px rgb(255 255 255 / 50%), 1px 1px 1px rgb(1 1 1 / 7%);
  89 |       background: transparent;

This sample project has component CSS as string literals inside the TypeScript source code files. This is a valid approach, but these bits of CSS were broken. For the first one, the paragraph style didn’t have a closing brace, exactly as the error message complained. Adding a closing brace resolved that error. The second stylesheet error used double-slash comment style which isn’t how CSS comments work. Changing that over to /* comment */ style resolved that error. After all of those changes, the little cipher app is up and running onscreen with some visual errors relative to what I saw for the same project StackBlitz.

How did StackBlitz run despite these problems? I’m going to guess that it has its own default tsconfig and did not require one to be specified. In the repository package.json I see that @angular/core was specified as “next“. Apparently StackBlitz interpreted that to be something new that included signals, whereas my local machine decided “next” is the same “15.2.9” as everything else which did not. As for the CSS syntax errors… I have no guesses and that remains a mystery to me.

But at least now I have something running locally, and I got a useful exercise understanding and fixing Angular build errors. Now onward to fixing runtime errors.


My code changes are made in my fork of the code lab repository in branch signals-get-started.

Installing “Getting Started with Angular Signals” Code Lab Locally

I revisited Compass, my Angular practice project, in light of what I’ve recently learned about Angular standalone components & other things. And now I’ll rewind back to Getting Started with Angular Signals code lab. I’ve gone through the primary exercise of Angular signals, but the app has many more lessons to teach me. The first one is: how do I fix a broken Angular build? Because while the code lab works fine on the recommended StackBlitz online environment, it failed to install locally on my development machine when I ran “npm install

npm ERR! code ERESOLVE
npm ERR! ERESOLVE unable to resolve dependency tree
npm ERR! 
npm ERR! While resolving: qgnioqqrg.github@0.0.0
npm ERR! Found: @angular/compiler@15.2.9
npm ERR! node_modules/@angular/compiler
npm ERR!   @angular/compiler@"^15.2.2" from the root project
npm ERR! 
npm ERR! Could not resolve dependency:
npm ERR! peer @angular/compiler@"15.1.0-next.2" from @angular/compiler-cli@15.1.0-next.2
npm ERR! node_modules/@angular/compiler-cli
npm ERR!   dev @angular/compiler-cli@"15.1.0-next.2" from the root project

Earlier, for the sake of doing the Angular signals code lab, I resorted to using error-free StackBlitz. Now I want to get it working locally. Checking the versions published to NPM for @angular/compiler package, I see 15.1.0-next.2 listed. @angular/compiler-cli also showed a 15.1.0-next.2 as a valid version. Their presence eliminated the “listing nonexisting version” as a candidate problem.

What’s next? I thought it might be how @angular/compiler has several different versions listed. Not just 15.1.0-next.2 that I checked, but also 15.2.9 and 15.2.2. Why don’t they match? Looking for the source of these version numbers, I looked through the repository and found they were listed in package.json file. I see 15.1.0-next.2 was explicitly named for the three @angular/[...] components under devDependencies, while “^15.2.2” is specified for all the @angular/[...] components under dependencies. That “^” prefix is apparently a “caret range” specifier, and 15.2.9 is the latest version that satisfies the caret range.

In order to make all @angular/[...] component versions consistent, I replaced all three instances of “15.1.0-next.2” with “^15.2.2“. That got me to the next error.

npm ERR! code ERESOLVE
npm ERR! ERESOLVE unable to resolve dependency tree
npm ERR! 
npm ERR! While resolving: qgnioqqrg.github@0.0.0
npm ERR! Found: typescript@4.7.4
npm ERR! node_modules/typescript
npm ERR!   dev typescript@"~4.7.2" from the root project
npm ERR! 
npm ERR! Could not resolve dependency:
npm ERR! peer typescript@">=4.8.2 <5.0" from @angular/compiler-cli@15.2.9
npm ERR! node_modules/@angular/compiler-cli
npm ERR!   dev @angular/compiler-cli@"^15.2.2" from the root project
npm ERR!   peer @angular/compiler-cli@"^15.0.0" from @angular-devkit/build-angular@15.2.8
npm ERR!   node_modules/@angular-devkit/build-angular
npm ERR!     dev @angular-devkit/build-angular@"^15.2.2" from the root project

Updating to 15.2.9 meant TypeScript “~4.7.2” is too old. Not fully understand what changed between those versions, I tried the lowest version listed as acceptable: 4.8.2. These version number changes were required to make them consistent with each other, and that is enough to bring me to the next error:

npm ERR! code ERESOLVE
npm ERR! ERESOLVE could not resolve
npm ERR! 
npm ERR! While resolving: qgnioqqrg.github@0.0.0
npm ERR! Found: @angular/animations@16.0.3
npm ERR! node_modules/@angular/animations
npm ERR!   peer @angular/animations@"^16.0.0 || ^17.0.0" from @angular/material@16.0.1
npm ERR!   node_modules/@angular/material
npm ERR!     @angular/material@"^15.2.2" from the root project
npm ERR!   peerOptional @angular/animations@"16.0.3" from @angular/platform-browser@16.0.3
npm ERR!   node_modules/@angular/platform-browser
npm ERR!     peer @angular/platform-browser@"16.0.3" from @angular/forms@16.0.3
npm ERR!     node_modules/@angular/forms
npm ERR!       peer @angular/forms@"^16.0.0 || ^17.0.0" from @angular/material@16.0.1
npm ERR!       node_modules/@angular/material
npm ERR!         @angular/material@"^15.2.2" from the root project
npm ERR!       1 more (the root project)
npm ERR!     peer @angular/platform-browser@"^16.0.0 || ^17.0.0" from @angular/material@16.0.1
npm ERR!     node_modules/@angular/material
npm ERR!       @angular/material@"^15.2.2" from the root project
npm ERR!     3 more (@angular/platform-browser-dynamic, @angular/router, the root project)
npm ERR!   1 more (the root project)
npm ERR! 
npm ERR! Could not resolve dependency:
npm ERR! @angular/animations@"^15.2.2" from the root project
npm ERR! 
npm ERR! Conflicting peer dependency: @angular/core@15.2.9
npm ERR! node_modules/@angular/core
npm ERR!   peer @angular/core@"15.2.9" from @angular/animations@15.2.9
npm ERR!   node_modules/@angular/animations
npm ERR!     @angular/animations@"^15.2.2" from the root project

Since I had upgraded my Angular tools to v16 for Compass, I now have a problem with this project which specified an older version. Now I have to downgrade with the following steps:

  1. Uninstalling Angular v16 via “npm uninstall -g @angular/cli
  2. Flush v16 binaries from my project tree with “rm -rf node_modules
  3. Installing 15.2.2 with “npm install -g @angular/cli@15.2.2“.

With these version numbers updated, I was able to run “npm install” successfully to install remaining dependencies.

How did this work on StackBlitz when I had problems on my local machine? I hypothesize that StackBlitz handles their installation procedure differently than a local “npm install“. If they ignore the “devDependencies” section, there wouldn’t have been a conflict with “15.1.0-next.2” modules. And if they ignored the caret range and used “15.2.2” exactly instead moving up to 15.2.9, there wouldn’t have been a TypeScript conflict either.

For now, I have solved my Node.js package management headaches and onwards to Angular headaches.


My code changes are made in my fork of the code lab repository in branch signals-get-started.

Notes on Angular “Forms” Guide

A quick survey of Google App Engine and other options for hosting Node.js web applications found a few free resources if I should start venturing into web projects that require running server-side code. That survey was motivated by the Getting Started with Angular Standalone Components code lab which included instructions to host our Angular app on Google App Engine. That example application also made use of form input, an Angular topic that I knew I needed to study if I were to build useful Angular apps.

As it’s such a big part of web development, Angular’s developer guide naturally included a section on forms. The first time I read the forms overview, I got as far as learning there were two paths to building forms on Angular: “Reactive Forms” and “Template-Driven Forms”. At the time, I didn’t know enough Angular to understand how they differ, so I put the topic aside for later. I almost managed to forget about it, because the Angular “Tour of Heroes” tutorial (which I took twice) didn’t use any forms at all! The standalone components code lab reminded me it’s something I needed to get back into.

This time around, I understood their difference: Reactive forms are almost entirely specified in TypeScript code, whereas template-driven forms are mostly specified via directives in HTML markup template. Template-driven forms resemble what I saw in Vue.js with v-model directives, allowing simple forms to be built with very little work. As scenarios get more complex, though, template-driven forms become limiting, and some features (like dynamic forms and strictly typed forms) are only available in reactive forms.

Both approaches use the FormControl class for underlying code functionality. Each instance corresponds to a single input field in the HTML form. As forms usually have more than one input field, they are collected in a FormControlGroup. A FormControlGroup can nest inside another as desired to represent a logical structure in the form. FormControlArray is an alternative way to group several FormControl together, focused on dynamic organization at runtime.

To validate data user has entered into the form, Angular has a Validators library that covers common data validation activities. Where equivalent HTML validation functionality exist, the Angular Validators code replaces them by default. Custom data validation logic would naturally have to be written in code, but they can be attached either programmatically in a reactive form or via directives in template-driven forms.

Common control status like valid, invalid, etc. can be accessed programmatically as boolean properties on AbstractControl (common base class for FormControl, FormControlGroup, etc.) They are also automatically reflected on the CSS classes of associated HTML input control with .ng-valid, .ng-invalid, etc. Handy for styling.

After reading through this developer guide, I’ve decided to focus on reactive forms for my projects in the near future based on the following reasons:

  • The sample applications I’ve already looked at for reference use reactive forms. This includes the recently-completed standalone components code lab and the starter e-commerce project.
  • My brain usually has an easier time reasoning about code than markup, and reactive forms are more code-centric or the two.
  • Data updates in reactive forms are synchronous, versus asynchronous updates in template-driven forms. If given the choice, synchronous code is usually easier for me to debug.
  • I like data type enforcement of TypeScript, and strictly-typed forms are exclusive to reactive forms. I like the ability to explicitly list expected data types (plus a null for when form is reset) via <type|null> and declare fields expected to be optional with the “?” suffix.

While I understood this developer guide a lot better than the first time I tried reading it, there are still a lot of information I don’t understand yet. Features like asynchronous validators are topics I’ll postpone until the time when I need to learn them. I’ve already got too much theoretical Angular stuff in my head and I need to get some hands-on practice before I forget them all.

Angular Standalone Components for Future Projects

Reading through Angular developer guide for standalone components filled in many of the gaps left after going through the “Getting Started with Angular Standalone Components” code lab. The two are complementary: the developer guide gave us reasons why standalone components exist, and the code lab gave us a taste of how to put them to use. Between framework infrastructure and library support, it becomes practical to make Angular components stand independently from Angular modules.

Which is great, but one important detail is missing from the documentation I’ve read. If it’s such a great idea to have components independent from NgModule, why did components need NgModule to begin with? I assume sometime in the history of Angular, having components live in NgModule was a better idea than having components stand alone. Not knowing those reasons is a blank spot in my Angular understanding.

I had expected to come across some information on when to use standalone components and when to package components in NgModule. Almost every software development design decision is a tradeoff between competing requirements, and I had expected to learn when using a NgModule is a better tradeoff than not having them. But I haven’t seen anything to that effect. It’s possible past reasons for NgModule has gradually atrophied as Angular evolved with the rest of the web, leaving a husk that we can leave behind and there’s no reason to go back. I would still appreciate seeing words to that effect from the Angular team, though.

One purported benefit was to ease the Angular learning curve, making it so we only have to declare dependencies in the component we’re working on instead of having to do it both in the component and in its associated NgModule. As a beginner that reason sounds good to me, so I guess should write future Angular projects with standalone components until I have a reason not to. It’s a fine plan but I worry I might run into situations when using NgModule would be a better choice and I wouldn’t recognize “a reason not to” when it is staring me in the face.

On the topic of future projects, at some point I expect I’ll grow beyond serving static content via GitHub Pages. Fortunately, I think I have a few free/trial options to explore before committing money.

Notes on Angular “Standalone Components” Guide

I went through the code lab Getting Started with Angular Standalone Components to see standalone components in action. The exercise provided a quick overview and a useful background to keep me focused while reading through corresponding documentation on the topic: Angular’s developer guide for standalone components. The opening paragraph advertised reducing the need for NgModule. Existing Angular applications can convert to using standalone components in a piecemeal fashion: it’s not an all-or-nothing choice.

Current standard Angular code structure packages one or more related components in a NgModule, which defines their shared configuration such as dependency injection. This made sense for solving problems like reducing duplication across similar components. Unfortunately, it would also bring its own set of problems which standalone components are intended to solve. Here is my current beginner’s understanding of these problems. (Likely with some mistake in the details):

The first and most relevant problem for Angular beginners like me is that every change to component dependency requires editing files in two locations. Beginners have to remember to jump back and forth: it’s not enough to add a new dependency in the component we are working on, we also have to remember to add new import references to the associated NgModule. (Which, for small beginner projects, is a single global NgModule.) This got to be pretty annoying when I was playing with adding Angular Material to Tour of Heroes tutorial.

Eventually an Angular developer would want to graduate beyond a single NgModule, at which point they’re faced with the challenge of code refactoring. Which dependencies were brought in for which components needed to be sorted out to see which imports can be omitted from a NgModule and which ones needs to be duplicated.

One motivation for splitting an Angular application into multiple NgModule is to speed up application load time. Load just what we need to run, when we need to run them. This is especially important on startup, put something up on screen as fast as possible for the user to know the application hasn’t frozen. Changing around which components are loaded, and when, is a lot easier when they are standalone and Angular introduced new lazy-loading mechanisms to take advantage.

There are other advantages to standalone components, but those three are enough for me to get an idea of the general direction. Enough motivation to learn all the new mechanisms introduced to replace what used to be done with NgModule. Starting with the root component, which every application has. If we want to make that standalone, we need to use bootstrapApplication() to define application-level configuration. Some framework level providers have added a standalone-friendly counterpart. A standalone component can use provideRouter() instead of importProvidersFrom(RouterModule.forRoot). The challenge is to find them in the documentation when we need them! I’m sure this will be a recurring issue as I intend to adopt standalone components for my future Angular projects.

Notes on “Getting Started with Standalone Components” Code Lab

I enjoyed the code lab exercise of Getting Started with Angular Signals, and I think I can learn even more from the exercise application source code. First up is learning “Angular Standalone”. I’ve seen mentions of this feature before, but I misunderstood it to be an advanced feature I should postpone until later. It wasn’t until an Angular presentation at Google I/O 2023 that I learned standalone components were intended to make Angular easier for beginners. My mistake! I noticed the Cipher sample project for Angular Signals were itself built with standalone components. Conveniently, the final page of that code lab pointed to another one: Getting Started with Standalone Components. So, I clicked that link to get my first look.

This code lab is a little bit older, about a year old judging from the fact it was written for Angular 14. But it wasn’t too far out of date because I was able to follow along on Angular 16 with only minor differences. That said, some Angular knowledge was required to make those necessary on-the-fly changes and I wouldn’t have known enough to do that earlier. Despite its bare-bones nature, template HTML in this code lab application used semantic HTML tags like <article> and <section>. I was happy to see that.

I learned a few things in addition to the main topic. As part of converting the boilerplate Angular application to use standalone components, I finally learned to recognized how Angular router is included in an app. Angular router is very powerful, core of component navigation and associated features like lazy-loading components. But it is big! For the sake of exercise, I took my newfound knowledge and built an empty Angular app without router. That came out to ~160kb from ng build. Adding the router back in and running ng build again got ~220kB of code. This means adding router ballooned size by over 37%. (220/160=1.375) Wow. At least now I think I know how to strip the router out of simple Angular applications that don’t need it.

This sample app also gave me another example of HTML forms in Angular. I’m still not very confident in building my own Angular app to take form input, but I think I understand enough to recognize this application is using the “reactive form” option in Angular. Not the “template-driven form” option which uses the NgModel directive that (at least to my limited knowledge) resembles Vue.js v-model.

And finally, this code lab sample application plugged Google Cloud services that have nothing to do with Angular standalone. “How to embed a chat dialogue in a standalone component using Dialogflow Messenger” and “How to deploy an Angular application to Google Cloud App Engine using Google Cloud CLI (gcloud)“. I’m probably never going to look at Diagflow Messenger again, but Google’s App Engine is interesting enough for a look later. Right now I want to follow this code lab with a dive into Angular developer guide for standalone components.

Notes on “Getting Started with Angular Signals” Code Lab

As part of Google’s I/O 2023 developer conference, they released several code labs that went with some of the talks. We can get a quick taste of new technology with these low-overhead exercises. I went through the WebGPU code lab out of curiosity to learn basics of modern GPU hardware programming. In contrast, I went into Getting Started with Angular Signals with more than just curiosity. I had full expectation I’ll learn stuff I can use in future Angular projects.

Project source code is set up for us to run in the browser (on StackBlitz infrastructure) with no need for local machine installation. Since I expect to use Angular Signals in future projects, I thought I’d clone the repo into my typical Angular environment: a Visual Studio dev container running locally. However, I ran into an error while running “npm install“:

While resolving: qgnioqqrg.github@0.0.0
Found: @angular/compiler@15.2.9
node_modules/@angular/compiler
  @angular/compiler@"^15.2.2" from the root project

Could not resolve dependency:
peer @angular/compiler@"15.1.0-next.2" from @angular/compiler-cli@15.1.0-next.2
node_modules/@angular/compiler-cli
  dev @angular/compiler-cli@"15.1.0-next.2" from the root project

This might have been easy to resolve with a little time, but StackBlitz was ready to go with no time. I decided to postpone debugging this situation until later and went through this code lab exercise on StackBlitz. It was fine, pretty much exactly what I saw in the video presentation associated with this code lab. We get to use signal() (the reactive piece of data), computed() (which reacts to other signal(s) and deliver its own reactive result) and effect() (which also reacts to other signal but does not deliver its own reactive result.) After going through Vue.js documentation recently, I recognize these as closely analogous to Vue’s data, computed, and watch.

If that’s all I got out of the code lab project, I would be disappointed because I hadn’t learn anything in addition to what was covered in the video presentation. But unlike the video presentation, I have the rest of the project in hand for me to sink my teeth into. The first thing I wanted to investigate was the “Angular Standalone” concept, which I had thought was an advanced technique. But one of the Angular seesions at Google I/O told me I was wrong: It was something intended to make Angular easier for beginners and small-scale projects. I should look into that! This Angular Signals code lab project makes use of standalone:true and I wanted to learn more. Fortunately for me, this code lab linked to an earlier code lab Getting Started with Standalone Components.

Notes on “Your first WebGPU app” Code Lab

I watched through a small set of the presentations Google released for their I/O 2023 event, some more immediately applicable to my potential projects than others. Since this is a developer conference, we get more than just talk: we get code to play with! Going back to my notes, I decided to start with the curiosity (versus applicable) end of the spectrum with “Your First WebGPU App” code lab to get a look at what GPU programming looks like.

Even though I have no intention of writing GPU code in the near future, I chose WebGPU as a starting point for learning because it was designed to run on all GPU hardware. And therefore, a lowest-common denominator design that has all of the modern GPU concepts and none of the proprietary details. Plus, now that it is a part of Chrome 113, the runtime is already installed on my machine. The code lab development environment is Glitch, running entirely within Chrome. While the code lab example is in JavaScript, it’s easy to do the same work with TypeScript by adding definitions. The biggest challenge to WebGPU development is that, because of how GPUs work, runtime errors would not throw a JavaScript exception with crash stacks. However, the WebGPU runtime will do its best to emit verbose information to the developer console, so we’re not totally left in the dark.

This code lab advertised no prior GPU programming experience is required, so it starts at the beginning introducing GPU programming concepts. Here I learn that “shaders” are functions that we write for GPU hardware to execute in parallel. Written in WebGPU Shading Language (WGSL) which has superficial resemblance (but is not) programming language Rust. In this code lab, WGSL snippets are opaque strings embedded in JavaScript. Looking at WebGPU samples outside of this code lab, I see WGSL can be standalone *.wgsl files. This enables development-time tools like Visual Studio Code extensions to help find mistakes early.

We learn about three types of shaders, who differ by the kind of data they are required to generate as outputs. Vertex shaders must return a point in 3D space. Fragment shaders must return a pixel color. Compute shaders are freed from specific return value requirements. I expect some WGSL commands are limited to certain shaders, but that was out of scope of this code lab. They did cover the fact different shaders have access to different kind of data inputs.

Shaders can read and write to GPU memory, but not all GPU memory are equal. Memory specifically designated for vertex data, for example, have advantages over more general types of “uniform” or “buffer” memory. Another GPU hardware concept is dividing work among multiple workgroups. Certain WGSL operations require shaders to be in the same workgroup. Which makes it tempting to put everything in a single workgroup, but doing so would hinder ability to run in parallel. Finding the best division of workgroups is out of scope of this course.

At the end of the code lab we have an implementation of Conway’s Game of Life, with vertex and fragment shaders that handle visual rendering and a compute shader to run the game algorithm. As a demo, it is not the most efficient implementation but it gives us a taste of GPU programming. A huge amount of setup work is required before any GPU processing takes place but, once running, it runs very quickly and in parallel. I think I’ve learned enough to keep in the back of my mind, so if an appropriate project comes to mind, I know to come back for more depth. For now I’m moving on to another code lab, one with lessons I expect to be more immediately relevant: Angular Signals.

Notes on Vue.js TypeScript and “Extra Topics”

As a beginner to Vue.js development I doubt I’d really need to worry about following best practices, but it was good to skim through that information just to see what lies down the road. Likewise, I didn’t expect to get very much out of the “Extra Topics” section of Vue.js documentation but I skimmed through it anyway to see what I can pick up. More than I thought I would, actually!

I haven’t done enough web development to understand all of the scenarios outlined in “Ways of Using Vue” but I do understand several of them. And I think I understood enough of the rest to recognize situations if they should come up in a “Ah, that’s what document was talking about” way. The overarching lesson is that: Vue is happy to work in several different shapes and sizes.

I enjoyed getting a look under the hood with “Reactivity in Depth“. I don’t understand enough about JavaScript proxies to use it in my own code, but it was good to see some of the benefits and pitfalls of working within Vue’s usage of the mechanism. It mostly boils down to the fact a proxy is one level of indirection from the real object. Unfortunately, one of the downsides appears to be that additional effort must be made to make sure TypeScript understands data types through this layer of indirection.

Coming from a background of strongly-typed languages like C, I was not a fan of the free-form chaos freedom of JavaScript. Using TypeScript imposes some order to the madness, and I appreciate that. Vue.js itself was written with TypeScript and supports TypeScript Vue.js application code, but doing so wasn’t as straightforward as I had thought it might be. This is especially true of the simpler Options API, partly because JavaScript usage was so simple that additional effort to be TypeScript-friendly seems like a huge imposition. In contrast, Composition API takes more effort to write but it also resembles “normal” code more so TypeScript was a better fit. After reading through TypeScript sections and “Composition API FAQ” I wonder if I’d be better off focusing on the more powerful and TypeScript-friendly Composition API for my own Vue.js projects. I’ll push that decision off to later.

The “Rendering Mechanism” and “Render Functions and JSX” sections were a look under a hood of a different part of Vue.js: outputting Vue.js component data into HTML markup for the browser. I’m not as familiar with this problem space, so I didn’t understand all the problems solved by these approaches. I don’t foresee myself writing custom rendering functions, and hopefully I won’t have to debug standard rendering functions.

I enjoyed seeing some of the fun things possible with CSS animations in “Animation Techniques” but that’s further off in the future. I would want to get a functional understanding of building Vue.js applications before I worry about visual polish, and I would lean on libraries before I start fiddling with details for myself.

Speaking of the future, I appreciated “Vue and Web Components” because it addressed one of my open questions: how much of frameworks like Angular or Vue will remain relevant in the future as web standards evolve? Many of web development frameworks arose to solve problems people had with browser-supported standards. These fed into evolution of web standards and eventually, browsers incorporated those lessons rendering many web tools no longer relevant. This “Vue and Web Components” section explained how it’s not a conflict, at least today. Vue has features not yet on the roadmap for standardized web components. Furthermore: peaceful coexistance is possible: Vue components can be self-contained into a standard web component for use elsewhere, and Vue applications can consume standard web components. This is encouraging, and it’ll be interesting to see this situation evolve in the future. For now, I’m going to wrap up this Vue.js learning session.

Notes on Vue.js “Best Practices”

As a Vue.js beginner, I doubt I’ll be building large scope projects in the near future. Despite that fact, I skimmed through solutions for scaling up a Vue.js application just to get a glimpse of what that involves. After that section of Vue.js documentation was “Best Practices”, and some of the items they called out were interesting.

When I tried out Vue.js Quick Start application, I was impressed by how small it was relative to the bare-bones boilerplate generated by Angular command line tools. I was curious what Vue.js would say about “Production Deployment” to make it even smaller and faster, and the answer is “not much”. The boilerplate build process with Vite apparently generates quite small code and any improvements would require domain-specific (and/or scenario-specific) optimizations.

Moving the focus from download size to runtime speed, there are a few tips for performance optimizations. As a Vue beginner, there weren’t much for me to absorb from this page. Either they are broad general optimization considerations that I’ve known from other contexts, or they dive into the deep weeds (what’s a shallowReactive?) that I didn’t yet understand.

I was glad to see Accessibility get its own section under Best Practices, but was mildly disappointed that its content appeared to be a rehash of general web accessibility concepts without any Vue-specific features. It would be nice if Vue directly help make building accessible sites easier, but I’ll settle for the topic at least getting mentioned even if generically.

For Security, it was great that their first and foremost item is Never Use Untrusted Templates. Good tip! About half of the rest of this page are variations on the same theme, giving examples of how user-provided information can be abused and how developers need to design against those abuses. Vue has minor protections in place, such as automatic escaping of strings, but it’s not foolproof. The developer must stay on guard.

These are all good tips, putting me in a good mood to finish off this set of Vue.js developer guides.

Notes on Vue.js “Scaling Up”

Reading through Vue.js documentation on “Built-in Components” I learned about powerful tools doing things a Vue application developer could not do on their own. But as a practically matter, I don’t think a beginner like myself would need to worry about them just yet. The same goes for the next section: “Scaling Up”, but I still wanted to skim through to see what scaling problems Vue thought deserved solving. This section was shorter than I had anticipated, because a lot of complexity has been delegated to other tools. These core Vue.js documentation pages would link to their documentation.

The section actually starts small by talking about Vue components and the Single-File Component (SFC) format because that is the basic unit of building large Vue apps. One of the first items is actually a pointer to scaling down via petite-vue for the progressive enhancement (sprinkle tiny bits of Vue in static site) usage scenario. Then it talks about tooling like an extension for editing Vue code in Visual Studio Code, and browser extension for debugging Vue code in browser.

The topic then moves to testing. Generic web application testing frameworks would work, but the Vitest test runner is optimized for unit testing Vue components. Once we get beyond unit tests, there’s a pointer to Cypress and a link to a Vitest vs. Cypress comparison.

For client-side routing, documentation gives a simple router implementation and a reference to Vue Router for a full power routing solution. For state management, we again have a simple implementation and a reference to Pinia for a more complete solution.

Server-side rendering (SSR) can get very complex, so the discussion started with the very important question: do we really need it? Sometimes goals can be satisfied with static-site generation (SSG) in which case we can use VitePress. But if actual SSR is required, there are multiple paths depending on the developer’s desire for control. Higher-level tools include Nuxt.js, which can also do static-site generation. And Quasar, which proclaims PWA capability as well as compiling to mobile apps and browser extensions. Plus it features its own Material Design compliant UI components, making it appear to be the “and the kitchen sink” solution. Which, depending on the project, may or may not be a fit for following best practices.

Notes on Vue.js “Built-In Components”

While reading Vue.js documentation on reusability, I learned of VueUse library filled with composable code available for use in our Vue apps. With that in mind, I was curious about the next section of documentation: “Built-in Components.” What’s special about these components? They must enable features requiring internal Vue support and could not be done (at least not efficiently) by an external code module. From what I can tell, they have one thing in common: they hook into platform changes to component structure.

First example supports transition animations. CSS defines a transition animation mechanism but that only applies to changing properties on an object. Animating the objects themselves being changed requires support like Vue’s <Transition> for individual components and <TransitionGroup> for elements in a list. By default, adding or removing components from the application means an abrupt and instantaneous change that may leave the user disoriented. These components allow designer to add transition animation to visually guide the user through such changes for a better user experience. Both by adding/removing classes to trigger CSS transition animation plus JavaScript hooks for whatever can’t be done via CSS.

An example of a feature that requires framework level support is Transition Mode, which manages how the old and new components interact to mitigate visually jarring artifacts. Moving from “mitigating a bad thing” to “enabling a great thing” is the Move Transitions demo for <TransitionGroup>, a great way to visually inform a user of changes in a list. After seeing that, I’ve become a fan of <Transition> / <TransitionGroup>. Sure, they can be abused just like all mechanisms designed to attract user attention (Die <blink> Die) but there’s plenty of room for subtle and tasteful designs. On the downside, I’m not a fan of Named Transitions which introduces more name magic to Vue.

In the spirit of Vue keeping things lightweight and not reinventing wheels, <Transition> / <TransitionGroup> only enables transitions, they do not define any animations themselves. This page links to animation libraries (Animate.css, GreenSock, Anime.js, Motion One) that it plays well with.

As much as I love some of the animations demonstrated here, as a practical matter I’m not likely to use any of these directly in the near future. If I start building projects with Vue, I’ll start without worrying about visual polish. For a first pass on visual polish, I’ll probably use something like Vue Material. Crafting my own visual styles with transition animations will be much further down the line, if ever.

I’m equally unlikely to use anything else in Vue’s “Built-in Components” section. <KeepAlive> keeps a <component> node alive to keep its state, in the expectation of eventual reinsertion back into the tree. <Teleport> moves visual elements somewhere outside of their proper location in Vue component hierarchy, useful for global model display dialog boxes. And <Sense> is still an experimental feature for consolidating visual feedback of multiple child asynchronous operations. (One spinning “waiting” animation instead of each component having their own.) <Sense> combined with <Transition> and <KeepAlive> to handle Vue Router changes is far too advanced of a technique for this beginner to worry about.

Which is true of most of the rest of Vue.js guides, but I wanted to skim over them anyway starting with “Scaling Up”.

Notes on Vue.js “Reusability”

Vue’js documentation’s “Components In-Depth” section gave me a pretty good idea of how Vue components are implemented and interact with each other. It’s a powerful mechanism of code organization and reuse, and I found it strange Codecademy’s “Learn Vue.js” course didn’t go into any details on componentization at all. Still, as useful as Vue components are, they can’t do everything and there are a few other mechanisms for code reusability in Vue.js. After reading “Reusability” section, my takeaway is that a beginner should know how to use these mechanisms: both to recognize their presence in example code and benefiting from work shared by others. In contrast, implementing these mechanisms is a more advanced topic a beginner can postpone until later.

The most significant page in this section is the first one: Vue Composables. These are self-contained packages of pure logic without a visual representation. In that regard it has some resemblance to Angular services but more limited in scope (which probably also means it is lighter weight.) It is possible to implement composable capability as a standard Vue component with no visual template (renderless component) but that incurs wasted overhead. For an even better idea of how a composable relate to other code reuse mechanisms, there’s a comparisons section on this page.

I was most fascinated by the async state composable example, because it seems to be a way to solve many of the problems RxJS wants to solve but with less of a learning curve. Also, this is optional versus RxJS which is required to make real use of Angular. But if we really want RxJS, there exist Vue composable to interoperate with RxJS. It is part of VueUse, a collection of Vue composition utilities that cover a lot of ground. I see stuff to help with concepts like an app going full screen, an app that wants to keep the device awake, and to help an app communicate over web sockets. Some of these aren’t terribly complex to implement on our own (like full screen) but using one of these composable component might be even easier.

Following the long and instructive page on Vue composable, there were two more pages far shorter in length. First is custom directives, a mechanism for installing and using code that needs direct low-level HTML DOM manipulation. It reads like a niche tool useful as a tool of last resort for things that can’t be done any other way. The page ends with “In general, it is not recommended to use custom directives on components.” And second page covers plugins, a mechanism to install functionality at the app level. We are warned to use plugins sparingly as too many of them start running into name conflicts and other general downsides of global code. This is partly because a plugin interacts with the rest of the app via non-plugin-specific mechanisms like Provide/Inject, custom directives, and attaching to global properties (app.config.globalProperties). The example on page shows two ways to do internationalization string plugin: attached to global properties, and provide/inject.

That’s a lot of different ways we can package Vue code to be reusable, but they’re limited in how they can participate in framework-level activities. For those, we need to use Vue’s built-in components.

Notes on Vue.js “Components In-Depth”

Vue.js documentation “Essentials” section ended with a page on “Component Basics”. That lead to the next section “Components In-Depth” covering more details on how and when to use Vue.js components and how they interact with other components in the app. After reading the section, a few items caught my attention that may or may not be significant once I start getting hands-on with Vue.

Naming

Names for Vue.js components are PascalCase by convention but that has problems with using them in HTML attributes that are liable to coerce everything to lowercase. The same problem applies to Vue.js events which are camelCase by convention. Vue.js includes magic to look at equivalent kebab-case names. I guess it solves the problem but seems like a source for future problems. What if multiple cased versions exist in code, which one runs first?

Name collisions could also happen due to a feature called “fallthrough attributes”. A component with its own “click” could have another “click” fall through from above. Which one has priority? A declared emitted “click” handler would override native “click”. A fallthrough “click” and an emitted “click” would… both be called? Wild.

I like that Vue.js works to magically fix up these kinds of problems, but I’m uncomfortable magic renaming mechanisms exist. Historically they were a high risk for bugs.

Props and Events

Props are intended to be one-way, from parent to child. But because it’s valid to pass objects, a child can modify those object instance and potentially change parent state. For performance reasons, Vue doesn’t try to detect or prevent such “deep change”. Documentation boils down to “Don’t do that” though it did describe a few potential scenarios and their workarounds. The right way for a child to communicate with parent is with emitted events, which are raised only on its direct parent.

Props go from parent to immediate child, emitted events are raised from child to immediate parent. Going beyond that (communicating across multiple layers in the hierarchy) could get messy. I saw mention for one mechanism called State Management, which will be covered in the “Scaling Up” section. There’s also the Provide/Inject mechanism, which isn’t exactly the same as Angular services but has a few similarities. To make them properly reactive values require computed() which is normally just for Vue.js Composition API. To read more about computed(), we are instructed to go back and read certain documentation sections while set to Composition API and not Options API.

Props can have default values when not assigned by parent and/or marked as “required”. To help keep objects well-behaved, we can validate values for props to ensure information coming in to a component is within expected range. We can also validate events, which isn’t a mechanism that shows up very often. Usually, events going out are assumed to be within expected range because it was generated within that component. Or maybe I’m misunderstanding the Vue.js mechanism.

Going Bigger

As mentioned earlier (in the context of state management) there’s an entire section dedicated to scaling up to larger and more complex projects. But we do get a preview of several mechanisms because they rightly belong in “Components In-Depth”

One item I had wondered about upon learning v-model was how that would work across component boundaries. The short version: Either (1) bind to a native input component inside the custom component template, or (2) bind to a writable computed value with getter and setter.

I think I have a good grasp of slots, but not scoped slots. It took me three readings to understand it is a (convoluted at first glance) way for information to cross parent/child component boundary. This will definitely take several rounds of hands-on practice (and painful debugging sessions) to master.

Symbols keys were brought up as a way to avoid Provide/Inject naming ambiguity in large projects because they potentially have global scope. This is the first I remember seeing symbol keys, I expect to get more information about it elsewhere in documentation.

Vue.js apps that grow big enough to worry about download size can split components off to be loaded asynchronously on demand. Vite recognizes this mechanism as a breakpoint for bundling purposes. I have yet to come across its Angular counterpart. Possibly lazy loading?


Building and using components are great, but they are not the end of the Vue story on code reusability.

Notes on Vue.js “Essentials”

I went through a short tutorial on Vue.js site and found it to be a succinct overview. It doesn’t go very deep in any single topic, instead introducing a breadth of Vue concepts with links for deeper reading. The recommended step to follow that tutorial is the documentation section labeled “Essentials”. It was instructive reading and some items I found notable were:

HTML

Vue.js essentials logically started with Creating an Application where I was happy to learn HTML is at the forefront acting as I thought markup should. An Vue application instance mounts to an element on the page. This is in contrast to Angular (and what I understood of webpack) where JavaScript is at the forefront and primary job of index.html is to load that JavaScript. It has almost no content of its own.

Because of this, innerHTML markup on Vue-bound components can act as template for that component. Which is convenient, but I wonder if there’s a way to have fallback text to show the user before Vue loads. Or if Vue fails to load because the user turned off JavaScript in their browser.

Another effect of this architecture is that it’s valid to have multiple Vue applications on a single page, each mounted on a different HTML container element. I don’t think I can do this with Angular, which I’ve only ever seen control the entire page.

Or maybe I can mount no Vue application at all? I don’t understand the nuts and bolts yet, but it seems feasible to let the markup load quickly for the user to see. And sometime after that, mount Vue components as needed.

A downside of this approach is that Vue might have stuffed too much into the HTML file. One example is Vue dynamic arguments, which look just like HTML attributes to the browser and is liable to get coerced to lowercase by browser’s parser causing “name not found” errors.

Reactivity

The Reactivity Fundamentals section was the first Vue documentation section where I saw large differences between Vue’s ‘Options API’ and ‘Composition API’ variations. Using options API means letting Vue handle everything behind the scenes, but using composition means the author has to be aware of what goes on behind that curtain and have to correctly participate in the process.

A core part of Vue 3 is the use of JavaScript proxy around component data so Vue knows when data has changed and need to react accordingly. Some notable side effects are that this pointer behaves slightly differently. We should avoid arrow functions and avoid tracking state outside of data. If we inadvertently share static data across instances, that will become a source of problems.

The reactivity infrastructure leads to a significant difference between computed properties and methods. Computed values are updated only when their reactive dependencies (through their proxies) are updated. This allows performance optimizations like returning a cached value rather than running the computation again. In contrast, methods are always called.

Compared to Angular, Vue’s reactivity system is much more constrained in scope. Nothing like Angular’s use of RxJS, which was its own big sprawling thing.

Components

The Vue Essentials section ends with Component Basics which lived up to its name covering the very basics of Vue components. Enough for us to understand how the various concepts tie together, even if we don’t understand them in detail just yet. For those that want to get deeper, there are plenty of links for more details.

Communication between components and their parents are usually handled in one of three channels: (1) Hosts can set value on a component’s props. (2) Component can emit events to handlers on the host. (3) Host can send template fragment via slots. Hosts can dynamically control what components are loaded with <component> which seems like a very powerful tool, illustrated with a simple tabbed interface where each tab is a different component.

Compared to Angular, I didn’t see a mechanism for code to interact with components beyond parent-child relationships. The good news is that there’s no counterpart to the headache of Angular service registration and injection. The bad news is that I don’t know how to get similar functionality in Vue. [UPDATE: I found Vue’s Provide/Inject.]

Infrastructure

Vue Playground is used for live code examples that we can play with in the browser. It seems to be an openly available tool, I didn’t see any restrictions constraining it to Vue documentation examples. This is a very promising option for experimenting with Vue concepts hands-on later.

At several locations, there were links to video courses on Vue School. Normally, I’m not a fan of video instruction but perhaps I should at least try a few of their free courses to see how well I can learn. The bar is high: it needs to be pretty impressive for me to start paying a subscription!


The “Essentials” section ends with a page “Component Basics”, good preparation for me to read the next section: “Components in Depth”

Notes on Vue.js Tutorial

After a brief detour exploring Vite’s support for legacy browsers like IE11, I returned to learn more about Vue.js from its own tutorial. Vue advertised itself as flexible in many ways and this is immediately visible on the first page of the tutorial: in the upper left corner we have two choices to make. Options vs. Composition API, and HTML vs. SFC format. Combined, it implies the tutorial can show us how to use Vue four different ways and would be a great resource for some direct comparisons.

SFC format integrate a component’s HTML template, CSS, and JavaScript/TypeScript all in a single *.vue file. These files are processed by a build-time tool like Vite to generate files actually going into a browser. In contrast, the HTML format is intended for using Vue without build tools. All of Vue is linked from the HTML and all script lives in the HTML as well for direct browser consumption.

Options API is the format used by Codecademy’s Vue.js course as well. It imposes a particular organization to Vue component data. Composition API does not impose such structure and so the JavaScript can be organized however you like but it also comes with requirement for managing overhead we wouldn’t have to worry about with Options API. In terms of expressive power, Options API is implemented using Composition API. Meaning anything we can do with Options we can do with Composition, but the reverse is not necessarily true.

For my first pass, I will leave things at default recommended for Vue beginners: Options API in SFC format. This tutorial is very short, with just 15 sections. Or more accurately 13 sections when accounting for the fact not much material is covered by the first page introduction or last page conclusion. The implementation structure is an in-browser learning environment similar in concept to Codecademy’s learning environment. The upside is that we don’t have to set up a local development environment, the downside is that we don’t get to see how a Vue development environment would look.

Vue’s tutorial covers a few important concepts with some overlap with Codecademy’s course (interpolation, directives, data/computed/method/watch) and some areas are different. It didn’t get into forms as Codecademy did, but did get into componentization, lifecycle hooks, and props/emits/slots which Codecademy’s course did not.

Both of those are shallower and more superficial than something like Angular’s “Tour of Heroes” tutorial, which went into far more depth starting with setting up a local development environment. If I want a Vue tutorial with that level of depth I will have to look elsewhere. Still, they were instructive and I’m glad I’ve gone through both Codecademy’s course and Vue.js tutorial. They prepared me to go deeper with Vue documentation starting with the “Essentials” section.


One tangential item I learned from this tutorial is the site https://jsonplaceholder.typicode.com/ for a publicly accessible free static mockup of generic API endpoints returning ipsum lorem data. This could come in handy for my own experiments in the future.