Running TypeScript scripts on Angular code

I needed to check my Angular application for data consistency. Those checks included completeness of internally referenced data, as well as the existence of files referenced by file name in that data.

Since Angular is TypeScript, it would be nice to use TypeScript for those checks, as I can directly reference all types and data declared in the application code using the import mechanism.

(I had previously used gulp for such tasks, but used JavaScript at that time. Apparently, there also exists a way to integrate gulp with TypeScript)

To run a TypeScript from the command line, I installed ts-node as a dev dependency in my Angular application:

npm i -D ts-node

However, running the script using

npx ts-node path-to-script.ts

would raise the error message

(node:1060467) Warning: To load an ES module, set “type”: “module” in the package.json or use the .mjs extension.
(Use node --trace-warnings ... to show where the warning was created)

Fortunately, this issue was quite easy to resolve thanks to this SO comment, as I only needed to edit tsconfig.json to add the following configuration to the JSON root:

"ts-node": {
  "compilerOptions": {
    "module": "commonjs"
  }
}

Angular Google Maps Demo

The Angular Components documentation on its Google Maps component does not contain a working demo. Probably that’s because it would need to incorporate a YOUR_API_KEY value…

So I took the sample code from their README.md and README.md files and stitched together a working (uhm api key) sample on StackBlitz.

I noticed that while my code works find on my local machine, the StackBlitz version will raise an error in the console

Error in src/app/google-maps-demo/google-maps-demo.component.ts (14:11)
Cannot find namespace ‘google’.

This error is caused by the fact that the namespace google.maps is only created if the Google Maps API is loaded successfully.

Which brings me to error handling:

Essentially there are 3 cases that need to be dealt with:

  • Success
  • Invalid API key
  • Invalid URL for the Google Maps API call

In case of an invalid API key, the <google-map> component raises the authFailure event

In case of an invalid URL, there is no script to initialize the google namespace, and you can check whether the objects window["google"] and window["google"]["maps"] exist.

The code is also available as a GitHub repository.

ng update will update you automatically

During my Angular Update Odyssey, I found that the Angular Update Guide frequently states

ng update will do this automatically for you.
ng update should take care of this automatically.
ng update will migrate you automatically.

and I wondered when ng update would actually perform all those magic tasks, because I did not notice them.

Then I had the relevation, that ng update only performs the update tasks, if the package update was successful.

If the package update fails due to package dependency conflicts, then no update magic. Bad luck. You need to clean-up by yourself.

Analyzing the update log I keep (essentially a copy-and-paste from the npx update console output), this is what happens:

> npx @angular/cli@10 update @angular/core@10 @angular/cli@10

The installed local Angular CLI version is older than the latest stable version.
Installing a temporary version to perform the update.
Installing packages for tooling via npm.
Installed packages for tooling via npm.
Using package manager: 'npm'
Collecting installed dependencies...
Found 54 dependencies.

Fetching dependency metadata from registry...
    Updating package.json with dependency @angular-devkit/build-angular @ "0.1002.4" (was "0.901.15")...
    Updating package.json with dependency @angular/cli @ "10.2.4" (was "9.1.15")...
...

  UPDATE package.json (2249 bytes)

npm ERR! code ERESOLVE
npm ERR! ERESOLVE could not resolve
npm ERR!
npm ERR! While resolving: my.web.app@1.0.0
npm ERR! Found: @angular-devkit/build-angular@0.901.15
npm ERR! node_modules/@angular-devkit/build-angular
npm ERR!   dev @angular-devkit/build-angular@"^0.1002.4" from the root project
npm ERR!
npm ERR! Could not resolve dependency:
npm ERR! dev @angular-devkit/build-angular@"^0.1002.4" from the root project
npm ERR!
npm ERR! Conflicting peer dependency: @angular/compiler-cli@10.2.5
npm ERR! node_modules/@angular/compiler-cli
npm ERR!   peer @angular/compiler-cli@"^10.0.0" from @angular-devkit/build-angular@0.1002.4
npm ERR!   node_modules/@angular-devkit/build-angular
npm ERR!     dev @angular-devkit/build-angular@"^0.1002.4" from the root project

My interpretation of the log entries is that

  • ng update fetches the dependencies of the new packages
  • ng update writes package.json with new version numbers
  • npm fails to install because of dependency conflicts
  • ng update gives up, because the new packages are not there

If you’re lucky, the node_modules directory has been deleted, too.

So, for future updates, I will consider the following update strategy:

  • run npx update
  • if the update fails as above, revert the changes in package.json
    – apparently there’s a reason npx update warns

Repository is not clean. Please commit or stash any changes before updating.

  • run npx update --force
  • commit changes
  • fix broken dependencies

mat- is not a known element

Updating an ancient Angular application to a more current version, I came across various warnings and error relating to the Angular Material framework.

Hidden behind the inconspicuous comment

Instead of importing from @angular/material, you should import deeply from the specific component. E.g. @angular/material/button

https://update.angular.io/?v=8.0-9.0

is the task of adding all kinds of import declarations to the modules containing components with Material elements or attributes.

In case of missing elements, the browser console will display a warning

‘mat-xxx’ is not a known element:
1. If ‘mat-xxx’ is an Angular component, then verify that it is part of this module.
2. If ‘mat-xxx’ is a Web Component then add ‘CUSTOM_ELEMENTS_SCHEMA to the ‘@NgModule.schems’ of this component to suppress this message.

In case of missing attributes, the browser console will display a warning

Can’t bind to ‘mat-xxx’ since it isn’t a known property of ‘<element>’

  • First, an import declaration in the module’s .ts file:
    import { MatXxxxModule } from '@angular/material/xxxx';
  • Second, adding the MatXxxxModule in the imports array of modules in the @NgModule declaration
Console Error MessageImport ModuleModule Location
mat-autocomplete is not a known elementMatAutocompleteModuleautocomplete
[matBadge]MatBadgeModulebadge
[mat-button]MatButtonModulebutton
mat-chip-list is not a known elementMatChipsModulechips
mat-dialog is not a known elementMatDialogModuledialog
mat-divider is not a known elementMatDividerModuledivider
mat-form-field is not a known elementMatFormFieldModuleform-field
mat-icon is not a known elementMatIconModuleicon
mat-list is not a known elementMatListModulelist
mat-menu is not a known elementMatMenuModulemenu
mat-paginator is not a known elementMatPaginatorModulepaginator
mat-progress-bar is not a known elementMatProgressBarModuleprogress-bar
mat-select is not a known elementMatSelectModuleselect
[mat-sort-header]MatSortModulesort
mat-spinner is not a known elementMatProgressSpinnerModuleprogress-spinner
mat-tab is not a known elementMatTabsModuletabs
mat-table is not a known elementMatTableModuletable
mat-toolbar is not a known elementMatToolbarModuletoolbar
[matToolTip]MatTooltipModuletooltip

Handling __ivy_ngcc_bak compiler errors

An Angular project I work on uses some custom libraries from a private repository. When making changes to the library, it is necessary to test locally, before publishing to the repository.

So how do you test your changes locally? I found it sufficient to copy the result of the ng-packagr script into the library’s directory of the project’s node_modules directory, run ng build, and you’re done.

This changed when Angular Ivy came along as we made the switch to Angular 10.

Suddenly, calling ng build after copying the packagr files resulted in multiple warnings stating

WARNING in Unable to fully load D:/path/to/web/node_modules/weblibrary/lib/filename.d.ts for source-map flattening: Circular source file mapping dependency: D:/path/to/web/node_modules/weblibrary/lib/filename.d.ts.map -> D:/path/to/web/node_modules/weblibrary/lib/filename.d.ts.map

and an error message

ERROR in Tried to overwrite D:/path/to/web/node_modules/weblibrary/lib/filename.d.ts.__ivy_ngcc_bak with an ngcc back up file, which is disallowed.

Well, I checked, and indeed, the file existed. Let’s delete the *.__ivy_ngcc_bak files, and run ng build again. I also found it necessary to delete the library’s __ivy_ngcc__ directory in the target project.

Run ng build again, and only the warnings are output. Run ng build once more, and the warnings are gone.


As I prepared this post, I wondered whether I had missing a solution that already exists for this problem.

I found npx install-from which seems to present itself as an alternative to npm link. I tried it, but unfortunately it stopped with an error message

D:\path\to\web>npx install-from D:\path\to\weblibrary\dist\weblibrary
npx: installed 5 in 3.016s
(node:14136) UnhandledPromiseRejectionWarning: Error: spawn npm ENOENT
at Process.ChildProcess._handle.onexit (internal/child_process.js:267:19)
at onErrorNT (internal/child_process.js:469:16)
at processTicksAndRejections (internal/process/task_queues.js:84:21)
(node:14136) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag --unhandled-rejections=strict (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 1)
(node:14136) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

without any indication what might have gone wrong.

Note that if you run a module which is not installed locally, npx will download it every time from your configured repository. If you use a package more often, better install it locally running npm install -g package.


So I checked again, and found that npm install also supports installation from a folder, not only from repository.

Running npm install D:/path/to/web/node_modules/weblibrary/package replaced the library’s <DIR> entry under node_modules with a <JUNCTION> entry (i.e. symlink) pointing to the path given as parameter:

Install the package in the directory as a symlink in the current project. Its dependencies will be installed before it’s linked.

In the package directory, installing the package adds a node_modules directory, and running ng build also updates the package.json file in the package directory. The application’s package.json entry for the package is updated from a version-specific reference to the library in the repository to a “file:…” reference to the package directory.


Now it became clear what the install-from is trying to do:

  • run npm pack in the library directory to create a .tgz
  • run npm install from the .tgz in the application directory
  • it fails somewhere

As a work-around to recreate the functionallity of import-form, I call npm pack package-directory from the library’s directory, which creates a library-version.tgz file, and npm install from the .tgz in the application directory.


So I came up with 3 methods to update a library in an application:

  • Clean-up Ivy artifacts and inject ng-packagr result using xcopy
  • npm install from library’s ng-packagr directory
  • npm pack to .tgz and npm install from .tgz

Fixing the Multi-Column Sort behavior of a Kendo UI Grid

Kendo UI for Angular contains a Grid component which also supports sorting multiple columns.

The sample grid only contains 3 columns, and sorting by multiple columns does not have much effect, but this is only about the sorting behavior, not the data being sorted.

As you check the “Enable multiple columns sorting” box, you’ll notice a behavior that I find counter-intuitive:

  • click on the Product Name column, and the grid is sorted by Product Name, showing 1 sort arrow
  • click on the ID column, and the grid is sorted by Product Name and then ID, indicated by the column order next to the sort arrows (Product Name – 1, ID – 2)
  • click on the Product Name column again, to reverse the sort order of the Product Name column.

What do you expect?

What the Kendo UI Grid does, it changes the order of columns so that the column clicked last ends up being the last in the order of columns.

My understanding is that the user wanted to change the direction of the sorted column (ascending vs. descending), and not change the order of the sorted columns.

Fortunately, the <kendo-grid> component provides a (sortChange) event, where you can implement your favorite sort behavior.

I created a multi-column sort sample on StackBlitz with my preferred sort behavior, the code can be viewed and forked here.