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.

Compass Project Updated to Angular 16, Standalone Components

After reading up on Angular Forms (both template-driven and reactive) I was ready to switch gears for some hands-on practice of what I’ve recently learned. My only Angular practice project so far is my Compass app. I couldn’t think of a reasonable way to practice Angular reactive forms with it, but I could practice a few other new learnings.

Angular 16 Upgrade

Every major Angular version upgrade is accompanied by a lot of information. Starting with the broadest strokes on the Angular Blog “Angular v16 is here!“, then more details in Angular documentation under “Updates and releases” as “Update Angular to v16” which points to an Angular Update Guide app that will list all the nuts and bolts details we should watch out for.

My compass app is very simple, so I get to practice Angular version upgrade on easy mode. Before I ran the update script, though, I thought I’d take a snapshot of my app size to see how it is impacted by upgrade.

Initial Chunk Files           | Names         |  Raw Size | Estimated Transfer Size
main.0432b89ce9f334d0.js      | main          | 642.10 kB |               145.70 kB
polyfills.342580026a9ebec0.js | polyfills     |  33.08 kB |                10.65 kB
runtime.7c1518bc3d8e48a2.js   | runtime       | 892 bytes |               513 bytes
styles.e5365f8304590c7a.css   | styles        |  51 bytes |                45 bytes

                              | Initial Total | 676.11 kB |               156.89 kB

Build at: 2023-05-23T23:37:24.621Z - Hash: a00cb4a3df82243e - Time: 22092ms

After running “ng update @angular/cli @angular/core“, Compass was up to Angular 16.

Initial Chunk Files           | Names         |  Raw Size | Estimated Transfer Size
main.6fd12210225d0aec.js      | main          | 645.17 kB |               146.60 kB
polyfills.f00f35de5fea72bd.js | polyfills     |  32.98 kB |                10.62 kB
runtime.7c1518bc3d8e48a2.js   | runtime       | 892 bytes |               513 bytes
styles.e5365f8304590c7a.css   | styles        |  51 bytes |                45 bytes

                              | Initial Total | 679.08 kB |               157.76 kB

Build at: 2023-05-23T23:50:05.468Z - Hash: f595c7c43b5422f4 - Time: 29271ms

Looks like it grew by 3 kilobytes, which is hard to complain about when it is 0.5% of app size.

Standalone Components

I then converted Compass components to become standalone components. Following the “Migrate an existing Angular project to standalone” guide, most of the straightforward conversion can be accomplished by running “ng generate @angular/core:standalone” three times. Each pass converts a different aspect of the project (convert components to standalone, remove vestigial NgModule, application bootstrap for standalone API) and we have an opportunity to verify our app still works.

Initial Chunk Files           | Names         |  Raw Size | Estimated Transfer Size
main.39226bf17498cc2d.js      | main          | 643.58 kB |               146.35 kB
polyfills.f00f35de5fea72bd.js | polyfills     |  32.98 kB |                10.62 kB
runtime.7c1518bc3d8e48a2.js   | runtime       | 892 bytes |               513 bytes
styles.e5365f8304590c7a.css   | styles        |  51 bytes |                45 bytes

                              | Initial Total | 677.48 kB |               157.52 kB

Since Compass is a pretty simple app, eliminating NgModule didn’t change very much. All the same things (declare dependencies, etc.) still had to be done, they just live in different places. From a code size perspective, eliminating NgModule shrunk app size down by about 1.5 kilobytes, reclaiming about half of the minimal growth from converting to v16.

Remove Router

Compass is a very simple app that really didn’t use the Angular router for any of its advanced capabilities. Heck, with a single URL it didn’t even use any Angular router capability. But as a beginner, copying and pasting code from tutorials without fully understanding everything, I didn’t know that at the time. Now I know enough to recognize the router portions of the app (thanks to standalone components code lab) I could go in and remove router from Compass.

Initial Chunk Files           | Names         |  Raw Size | Estimated Transfer Size
main.4a58a43e0cb90db0.js      | main          | 565.24 kB |               128.77 kB
polyfills.f00f35de5fea72bd.js | polyfills     |  32.98 kB |                10.62 kB
runtime.7c1518bc3d8e48a2.js   | runtime       | 892 bytes |               513 bytes
styles.e5365f8304590c7a.css   | styles        |  51 bytes |                45 bytes

                              | Initial Total | 599.14 kB |               139.94 kB

Build at: 2023-05-24T00:30:36.609Z - Hash: 155b70d2931a3e06 - Time: 18666ms

Ah, now we’re talking. App size shrunk by about 77 kilobytes, quite significant relative to other changes.

Fix PWA Service Worker

And finally, I realized my mistake when playing with turning Compass into a PWA (Progressive Web App): I never told it anything about the deployment server. By default, a PWA assumes the Angular app lives at the root of the URL. My Compass web app is hosted via GitHub Pages at https://roger-random.github.io/compass, which is not the root of the URL. (That would be https://roger-random.github.io) In order for path resolution to work correctly, I had to pass in the path information via --base-href parameter for ng build and ng deploy. Once I started doing that (I updated my npm scripts to make it easier) I no longer see HTTP 404 errors in PWA service worker status page.

I’m happy with these improvements. I expect my Compass web app project will continue to improve alongside my understanding of Angular. The next step in that journey is to dive back into the Angular Signals code lab.


Source code for this project is publicly available on GitHub.

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.

Window Shopping Google App Engine (And Some Competitors)

I think I have a working understanding of Angular Standalone components and plan to use them for my future Angular projects. But the sample application in “Getting Started with Angular Standalone Components” code lab had other details worth looking into. Two Google cloud services were included in the exercise: Diagflow Messenger and App Engine. I assume their inclusion were a bit of Google product cross-promotion, as neither were strictly required to build Angular applications using standalone components.

Diagflow Messenger makes it easy to incorporate a chatbot onto your page. I personally always ignore chatbots as much as I can, so I’m not likely to incorporate one to my own project. On the other hand, Google App Engine looks interesting.

The standalone component code lab project only used App Engine to host static files via express.static. In that respect, it could have just as easily been hosted via GitHub Pages, which is what I’ve been using because my projects so far have been strictly static affairs. But I have ambition for more dynamic projects in the future and for those ideas I’ll need a server. When I took the Ruby on Rails Tutorial (twice) aspiring web developers could host their projects on Heroku’s free tier. Unfortunately, Heroku has since eliminated their free tier. Web development learners like me will have to look elsewhere for a free option.

Heroku implemented their service on top of Amazon Web Services. Looking around AWS documentation, I didn’t find an equivalent service making it trivial to deploy backend projects like Ruby on Rails or Node.js. We could certainly roll our own on top AWS EC2 instances and there’s a tutorial to do so but it’s not free. While there’s an EC2 introductory offer for 750 free hours in the first 12 months, after that there’s nothing in Amazon’s always-free tier for this kind of usage.

Google App Engine is similar to Heroku in offering developer-friendly way to deploy backend projects, in this case Node.js. And even better, basic level services are free if I stay within relevant quotas. According to documentation, this service is built on containers rather than EC2 virtual machines like Heroku. I don’t know if there’s enough of a difference in developer experience for me to worry about. One footnote I noticed was the list of system packages available to my projects. I saw a few names that might be useful like FFmpeg, ImageMagick, and SQLite. (Aside: The documentation page also claims Chrome headless, but I don’t actually see it on the list.)

To round out the big three, I poked around Microsoft’s Azure documentation. I found Azure App Service and instructions for deploying a Node.js web app. Azure also offers a free tier and it sounds like the free tier is also subject to quotas. Unlike Google App Engine, everything on Azure comes to a halt if I hit free tier quotas. For experiments, I prefer Azure’s approach of halting. Exceeding free tier limits on Google App Engine starts charging money rather than taking my app offline. If I build something I want to actually use, I might prefer Google’s method. Sure, I might have to pay a few bucks, but at least I can still access my app.

These two free plans should be good enough for me to learn and evolve my skills, assuming they don’t disapper as Heroku free tier did. If I get far enough in my web application development adventures to be willing to pay for server capacity, I’m will also look into an independent offering like DreamHost’s DreamCompute or one of DigitalOcean’s products for Node.js hosting.

But that’s for later. Right now, I have more I could learn from Angular standalone components code lab sample application.

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 Google I/O 2023: Angular Signals

It was reassuring to learn that Google Chrome team is continuing to invest in debugging tools for web developers, but I need to practicing writing more web apps before I have anything to debug. My final set of Google I/O presentations focus on Angular framework, which I’ve been learning about and intend to use for future projects.

What’s new in Angular

The biggest news for Google I/O 2023 is the release of Angular 16, and the rundown included a mixture of features I understood and features I did not. One of the latter is “Angular Standalone” which I knew was introduced in an early state in Angular 15. I had mentally filed it away as an advanced feature I could postpone to later, but these presenters said it was intended to reduce Angular learning curve. Oh yeah? Maybe I shouldn’t have shelved it. I should take a closer look.

New to Angular 16 is “Angular Signals“. I saw mentions of this when I was learning RxJS, but all I saw at the time were excitement from reactive programming proponents. Listening to its overview in this presentation, I quickly realized this was Angular catching up to what Vue.js had with its reactive data system. For more details, we were pointed to another session…

Rethinking reactivity with Signals

This session covered Angular Signals in more detail. Conceptually, I understood an Angular signal is equivalent to fields in Vue data(). An Angular “computed” is a counterpart to members of Vue’s computed, changing in response to signal changes. And finally, Angular’s “effect” is analogous to Vue’s watch, executing code in response to signal changes.

While concepts may be similar, Angular’s implementation have some significant differences. For example, Angular is all-in on TypeScript and incorporating type information in Angular Signals didn’t feel like a hacked-on afterthought like it did for Vue data. As a fan of TypeScript, I found this very encouraging.

Getting started with Angular Signals

After concepts of Angular Signal were introduced and explained, this presentation follows up the theory with some hands-on practice. This presentation is a companion to the code lab on Angular signals, building a sample cipher game. I want to go through that code lab myself and learn the primary lesson, and I expect I can learn even more from that exercise beyond the main lesson of Angular signals. But first, I’m curious to learn what GPU programming looks like with WebGPU.

Notes on Google I/O 2023: Browser Debugging

It was fun to see which advanced browser capabilities Google wanted to call out in their I/O conference, but advanced capabilities or not, developers need to test and debug code. I checked into a few sessions about Chrome Developer Tools.

Learn how Chrome DevTools features help developers debug effectively

That title is a pretty big promise covering a huge area, so naturally a 14-minute video couldn’t deliver everything. The title page showed a more modest and accurate title “Reduce debugging friction.”

One thing I noticed when I started working with web frameworks like Angular and Vue is that I can’t find the code I wrote anywhere. Part of this is expected: TypeScript is not directly executed but translated into JavaScript for the browser. But that translated code is buried within a lot of framework code.

To make debugging easier, Chrome team recognizes the reality that what the developer sees in the browser debug window has little resemblance to what they wrote. C developers know well that when things crash into the debugger we’d be looking at optimized assembly code and not our C source code. Debug symbols allow C code to be matched up against their assembly code output, and similarly browsers support “Source Maps” to provide this correlation between “Authored View” (the code developer wrote) and “Deployed View” (the code being executed by browser.)

A few other browser debug features were covered: the ability to mark code to be ignored, useful for decluttering our screen of library code we don’t care about at the moment. Native code debugging concepts like conditional breakpoints are also available in browser debugger. Another breakpoint derivative are “logpoints” which has all the usefulness of adding console.log() into our code without having to modify our code.

There was a brief blurb about a recorder function, whose output can then be used by Puppeteer. I thought this was a great way to document steps to reproduce bugs, making it much less ambiguous than writing text in a bug ticket. A little bit later, I realized this also meant we could incorporate those recorded steps into an automated regression test. Now that would be awesome. And speaking of browser automation…

WebDriver BiDi: The future of cross-browser automation

This presentation gave us a quick overview of browser automation past, present, and future. The future being represented by the WebDriver BiDi browser automation protocol. Still in development, but on track to be a cross-browser solution of the future. (Not just on Chrome.) I’ve barely dabbled in Selenium, but I knew enough to understand bidirectional communication between the test host and browser under test will open up a lot of benefits to make tests more consistent and waste less time waiting.

Build better forms

Here’s another session with a title far more grandiose than the actual topic. I have an alternate title: “How to make your site work with Chrome Autofill.” There’s more than Google’s self-interest at hand, though. A form crafted so a Chrome autofill recognizes its semantics also means the browser accessibility tools would understand it as well. Two birds, one stone. Most of this session boils down to following standardized form autocomplete hints, and Chrome developer tools to help you get there. I’ve barely done any web development and I’ve already learned a dislike for how complex and annoying forms can be. Every framework tries to make forms less annoying and I’m sure Angular has something to offer there but first I want to see what’s new and shiny in Angular.

Notes on Google I/O 2023: Advanced Web Browser Capabilities

After going through several Google I/O sessions on advancements in browser UI and animation, I headed over to a different section of web browser evolution: advanced hardware capabilities for performance and/or connectivity.

Introducing WebGPU

WebGPU recently became publicly available in a mainstream browser in the form of Chrome 113, and varying levels of support are underway for other browsers. This is a topic that has interest and support from many different parties and I’m cautiously optimistic it will become widespread in the future. WebGPU is an evolution from WebGL following the precedence of desktop APIs Vulkan/D3D 12/Metals as evolutions from OpenGL.

One major aspect of this evolution was growing beyond just drawing operations, opening up hardware capabilities to non-drawing applications. Today this interest is focused on running machine learning algorithms, but it is more general-purpose than that. (Side question: how does that relate to WebML? Something to look into later.) Today people have shoehorned ML workloads into WebGL by rephrasing computation as drawing operations, with associated limitations and conversion overhead. WebGPU eliminates such pretense.

Another major aspect is elimination of global state. Global state was fine when the focus of OpenGL was to support a single CAD rendering window, but very problematic in today’s world of large code modules running in parallel on different tasks.

One distinction between WebGPU and its counterpart desktop APIs is that absolute raw performance does not override everything else. Some performance were left on the table in favor of gaining consensus across partners in standardization, in order to reach better cross-platform compatibility. I found this very interesting and promising for the future.

This presentation had an associated Codelab “Your first WebGPU App” to build a parallelized Conway’s Game of Life, no prior GPU experience required. I don’t know what I might use WebGPU for, but I’ll add this Codelab to my to-do list just to see what WebGPU is like firsthand.

WebAssembly: a new development paradigm for the web

In contrast to the hot-from-the-oven WebGPU, WebAssembly has been around for a while. Letting browsers run code beyond JavaScript. The initial scenarios were to allow legacy C code to be compiled into WebAssembly for high performance browser execution, and that approach has found success. I knew about Google’s TensorFlow AI runtime running on WebAssembly, but I hadn’t know there were also ports of codebases like OpenCV and FFmpeg. That might be worth looking into later.

With success of C and similar code that manage their own memory, recent attention had turned to supporting managed-memory code efficiently. Beyond JavaScript, that is, since JavaScript is already a language where the runtime manages the memory on behalf of the developer. And when another such runtime is in the WebAssembly box, that meant two different memory managers. At best they are duplicating effort and wasting memory, at worst having two separate reference counting garbage collection systems risk them falling out of sync to cause memory errors. This presentation gave a few examples of the kind of messes we can get into, now resolved with latest WebAssembly evolution putting all memory management under a single system.

One thing thrown out as an aside was a comment “Node.js or other webasm server environments” but no further elaboration in the presentation. WebAssembly on the server? Sure, why not. I don’t know all the reasons why people might be interested but I also didn’t expect Node.js (JavaScript on the server) to be interesting and I was very wrong. If server-side WebAssembly takes off, I’ll cross paths with it soon enough.

Advanced web APIs in real world apps

I watched this presentation because I was curious about latest progress in Project Fugu, Google’s effort to bridge the capability gap between web apps and native apps. The magnetometer web API, which I recently played with, was done under this umbrella. As did many other electronics hobbyist friendly technologies like Web Serial and Web USB.

This session didn’t cover any capabilities that were personally relevant to me. They were selections from the Project Fugu Showcase, demonstrating capabilities like local file system access, use fonts installed on the system, etc. No hardware related fun this time, oh well. I moved on to a different topic: tools to help debug web apps.

Notes on Google I/O 2023: CSS Viewport and Animations

After watching a few Google I/O presentations strictly for curiosity’s sake, I proceeded to a few sessions that might have some relevance on near-term project ideas. First up are a few sessions that cover new developments in web browser capabilities. Generally speaking, I expect I’d only use most of these indirectly via libraries, but it’s neat to see what’s possible.

What’s new in web

This was a quick overview of new web development capabilities that, as tracked by Web Platform Baseline, have enough support to become realistic option for deploying in production. The part that caught my attention were CSS viewport units that lets us account for space consumed by browser UI. More details were promised in another session “What’s new in Web UI” so that’s where I went next.

What’s new in Web UI

I got a little more information about CSS viewport units here: not just small (with all browser UI present) and large (if all browser UI were absent) but also dynamic (adjusts as UI pieces moves in and out.) Nice! Related to this front are container queries that layout decisions to be made at a finer-grained level: parent container, instead of entire viewport.

CSS nesting folds a major advantage of Sass into standard CSS. Cascade layers and scoped styles allow fine control over style sheet cascading and avoid collisions in the face of modern web platforms that combine all styles from components and bundle them into a single download.

We’ve always had window.alert() to ask the browser to pop up a modal dialog box, but it’s very crude. Trying to recreate that illusion inside our web page required a lot of nasty DOM hackery. Popovers are still experimental, but it promises all the refinement of HTML under author’s control while letting the browser & operating system handle all the windowing complexity.

A few quick demonstrations of nifty animations were given, with a pointer to another session “Whats new in web animations”.

What’s new in web animations

This presenter opened up with a great manifesto on how things should work: “animations that help users build an accurate mental model of how the interface works, thereby increasing the overall usability.” When it works well, the user would rarely even notice their presence. What we usually notice to our annoyance are zealous overuse of animation getting in our way!

One incoming experimental feature is the View Transitions API for managing CSS animation effects as it applies to elements entering and leaving the markup DOM structure. It caught my attention because this would be a standardized version of what I just saw in Vue with <Transition> and something I’ve found mentioned in Angular Developer Guides as well.

Most of the effects demonstrated here are things I’ve seen online with other websites, but now it can be done strictly with CSS. No JavaScript required, which is great for moving workloads off the main thread.

These are all good things coming down the line for visual layout, but usually I’m more interested in hardware interfacing browser capabilities.

Notes on Google I/O 2023: AR, Material 3, ChromeOS Kiosk

Google I/O 2023 developer conference materials are available online free of charge. Back when they were in-person events with an admission fee, I never felt I would benefit enough to be worth the cost and effort. But now the price is but a few hours of my time. I looked around to see what information I can absorb. I started with sessions that were just for curiosity, with no short-term plans to get hands-on.

What’s new in Google AR

I examined Google’s ARKit earlier with an interest in adopting its structure-from-motion capabilities for robotics projects. I had hoped its “recognize things in the room for users to interact” capability can be used for a robot “recognize things in the room so robot doesn’t run into them.” The two intents were different enough I didn’t see a promising path forward. I thought I’d watch this presentation to see if that has changed. The answer is “No”, but it was still fun to see what they’re up to.

This video explained they are focusing on users wandering outdoors in an urban environment. Think Google Street View but augmented in 3D with “Streetscape Geometry”. Their updated “Geospatial Depth” and “Scene Semantics API” are optimized to work with street scale landmarks, not indoor rooms like I wanted for my robots. There’s a separate session on the “Geospatial Creator” tool available to create AR content at this scale. As part of I/O they’ve released an AR demo “Mega Golf” that lets you play a game of golf through your real-world cityscape. Another showcase sometime later this year will be an Pokemon-Go style AR update to an old classic with “Space Invaders World Defence.” I’ll probably give those apps a whirl but won’t do much more until/unless a cool project idea arises.

What’s New in Material Design

I’ve long been fascinated watching Google evolve their “Material Design.” Their latest push is in creating “Spirited UI” to evoke positive user emotions via animation. They’re also making an emphasis on letting individual designer establish their own unique look, deviating from rigid design rules. “Instead of laying down rules, it’s laying down a beat.”

I got pretty lost in the artistic design language, but I understood the engineering aspects. The primary platform for this design team is Android Jetpack Compose, followed by View and Flutter. Web is… somewhere after that and not mentioned in the presentation. I keep an eye out for future developments.

Developing Kiosk Apps for ChromeOS

I’ve been interested in building web apps for single focused tasks. My Compass practice project is pretty kiosk-ish with its singular focus and full-screen experience. There was also an experiment earlier investigating using a Raspberry Pi to serve up a kiosk experience. I wanted to check out what ChromeOS has to offer on this front.

I only got partway through the session, stopping after they listed a ChromeOS management license requirement to enable kiosk functionality. Either Chrome Enterprise (nope) Chrome Education (nope) or a Kiosk & Signage license at $25 per device per year. More than I’m willing to pay for idle curiosity, so I’m moving onwards to other presentations.

Vue.js Beginner Learning Checkpoint

I’ve just finished reading the top-level set of Vue.js guides published on their documentation site. This followed a quick hands-on run through their Quick Start application and tutorial. I learned a lot about what Vue is (and just as importantly, what it is not.) As an added bonus, it hasn’t been too long since I went through their Angular framework counterparts. (Shopping cart on StackBlitz, Tour of Heroes hands on, and a few developer guides.) Running through these two frameworks back-to-back lets me compare and contrast their design decisions. See how they solve the common problems web developers encounter.

Vue.js is very flexible in how it can be deployed, making it a great option for developers who find Angular’s all-in-one package too constrictive for their preferences. Vue is also lighter weight: an empty Vue app is a tiny fraction of the size of equivalent empty Angular app, and this was possible because Vue is a newer framework with finer-grained modularization. Newer means there’s less legacy compatibility code to carry forward, and some framework level features are no longer necessary because they are included in newer browsers. More extensive modularization means some features inherent to Angular (and thus must be part of an empty app) are optional components in Vue and can be excluded.

But such flexibility also comes with downsides for a beginner, because every “you are free to do what you want” is also a “you have to figure it out on your own.” This was why I got completely lost looking at Polymer/Lit. I thought Vue occupied a good middle ground between the restrictive “do it our way” Angular design and the disorienting “do whatever you want” of Polymer/Lit. In the short term I think I will continue learning web development within Angular, because it is a well-defined system I can use. If I stick with learning web development, I expect I’ll start feeling Angular’s rigidity cramps my style. When that happens, I’ll revisit Vue.

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.