Archive for 2021

February 25th, 2021
12:23 pm
Angular 11 – remote loading of Angular Element from separately deployed webpack bundle

Posted under Angular & Uncategorized & Web & Webpack

This follows on from my original post here for Angular 6.

In Angular 11, the up-and-coming approach to this is to use Module Federation for remote loading of microapps/micro-frontends. Manfred Steyer has a new article series on this whole approach here, and the second article about using Angular for this may be found here.

My issue with module federation at present is that it is still appears too nascent to simply ‘just work as standard’ with angular – the above posts need a number of workarounds including the use of yarn, and I was unwilling to switch to this method just for my present requirement which is just the generation of simple proof of concept work.

At present, I am therefore continuing to use my original approach as above for my POC work, as it still works with Angular 11. I have tweaked the approach a little to allow performing production builds, by disabling the hashes added to the filenames (I already use my own alternative method for cache busting/cache avoidance using a query string parameter). Details are shown below.

package.json scripts section

{
"name": "microapp-fabric-prototype",
"version": "0.0.0",
"scripts": {
"ng": "ng",
"start": "ng serve",
"prebuild": "npm run scss",
"build": "ng build",
"postbuild": "node post-build.js",
"test": "ng test",
"lint": "ng lint",
"e2e": "ng e2e",
"scss": "npm run scss1",
"scss1": "cd src/assets/resources/themes/salient-v11-violet && sass theme.scss:theme.css --source-map .",
"prebuildProd": "npm run scss",
"buildProd": "ng build --prod --output-hashing none",
"postbuildProd": "node post-build-prod.js"
},

post-build.js

/* Do post build actions on the webpack bundle.
* Rename the webpack jsonp objects throughout, to prevent invalid multiple bootstraps due to name collisions
* This is due to an angular bug - see here: https://github.com/angular/angular/issues/23732
* This script requires the npm replace package: https://www.npmjs.com/package/replace
* To install use: "npm install replace -g" */

var replace = require("replace");

doWebpackReplace('MicroappFabric','microapp-fabric-prototype',
['main.js', 'polyfills.js', 'runtime.js', 'vendor.js']);

function doWebpackReplace(name, filename, paths) {
replace({
regex: '\\["webpackJsonp"\\]',
replacement: `["webpackJsonp${name}"]`,
paths: paths.map((p) => `dist\\${filename}\\${p}`),
recursive: false,
silent: false,
});
}

post-build-prod.js

/* Do post production build actions on the webpack bundle.
* Rename the webpack jsonp objects throughout, to prevent invalid multiple bootstraps due to name collisions
* This is due to an angular bug - see here: https://github.com/angular/angular/issues/23732
* This script requires the npm replace package: https://www.npmjs.com/package/replace
* To install use: "npm install replace -g" */

var replace = require("replace");

doWebpackReplace('MicroappFabric','microapp-fabric-prototype',
['main.js', 'polyfills.js', 'runtime.js']);

function doWebpackReplace(name, filename, paths) {
replace({
regex: 'window\\.webpackJsonp',
replacement: `window.webpackJsonp${name}`,
paths: paths.map((p) => `dist\\${filename}\\${p}`),
recursive: false,
silent: false,
});
}

At present, I will continue to monitor the situation with Module Federation, and will likely switch to using it when it really is working ‘out of the box’ with no tweaks or complications.

 

Comments Off on Angular 11 – remote loading of Angular Element from separately deployed webpack bundle

February 19th, 2021
6:05 pm
PrimeNG 11/Angular 11 build fails due to missing @angular/cdk dependency

Posted under Angular & PrimeNG & Uncategorized & Web
Tags , ,

… as above – I received the following error in relation to a PrimeNG p-dropdown used with a p-dataview:

Error: The target entry-point “primeng/dataview” has missing dependencies:
– @angular/cdk/scrolling

This issue is covered here.
I haven’t seen this before, but installing @angular/cdk resolved the build failure. This appears to be a transitive dependency issue which should have been resolved by my dependencies on PrimeNG.

 

Comments Off on PrimeNG 11/Angular 11 build fails due to missing @angular/cdk dependency

February 12th, 2021
5:33 pm
Dynamic Typing issues with Typescript/Angular 11

Posted under Angular & TypeScript & Web
Tags ,

I was using a JSON file to hold external configuration for an agular application, using the pattern detailed previously here, and the loading mechanism detailed here.

The configuration pattern used allowed parameters to be overridden dynamically from query string parameters, so that for example you could choose an application theme in your URL.

I wanted to further extend this to allow sticky configuration parameters using localStorage, in particular to support a sticky pinnable title banner as detailed previously here. The aim was that both the query string support and the sticky localStorage support would be encapsulated in the configuration pattern and all transparently handled by the existing configuration service. The service would be enhanced with a new method call to allow persisting of a sticky parameter to localStorage and to the loaded config, for example for my sticky pinnable banner, to allow calling to persist the new pin state when the pin state was changed via the pin button. The previous pin state would then be loaded when the site was subsequently visited. Sticky parameters would override settings in the JSON file, and query string parameters would further override both of the former, and a query string parameter would also be sticky and would persist to localStorage, so for example the previous theme selected in the URL would be remembered.

One challenge was that whilst the configuration structure conformed to a Typescript interface, I needed to dynamically update configuration parameters from query string parameters or localStorage. This meant using dynamic keys e.g. based on the querystring parameters. Whilst this was initially possible by casting the config structure and using a map style lookup with config[keyVariable], it was not possible to dynamically infer the type of a configuration parameter from the interface, noting that both query string and localStorage parameters are strings. Often this is not a problem, as the default javascript behaviour will quite often cope, but therein lies a danger. In one case with a boolean, my query string/localStorage parameter was of course a string, and when written to the configuration using a dynamic key name as above, it was written as a string rather than the boolean expected in the interface. This meant that the value ‘false’ was in fact interpreted as true, due to the standard javascript truthy behaviour whereby a non empty string counts as true.

In the end, my solution was to have a subsection in the configuration, called dynamicParams, which contained all these dynamic parameters. It was therefore a requirement that all the dynamic parameters were strings and that client code reading the configuration had to be aware of that and convert as required.

One useful feature was to use Typescript intersection types for the dynamic parameters section, as detailed here. This allowed me to specify the dynamic parameter names explicitly, to allow them to be read in code directly as for example using config.dynamicParams.theme. I could also use programmatic access as above using config[keyVariable].

The section of the interface definition to do this was as follows:-

export interface AppConfig {
   appTitle: string;
   description: string;

   /* These parameters can be overridden from URL query parameters
    * and/or sticky local storage parameters
    * They must all be strings as are accessed by key programmatically */
   dynamicParams: {[key: string]: string | undefined} &
      {
         theme: string;
         pinBanner?: string;
         profileId?: string;
      };
   propertyDetail: {
      defaults: {
      photoLinkHoverText: string;
   }
   …

A particular point of note was that the intersection type using “&” was needed for this to work, rather than the union type “|”.

This solved the problem satisfactorily and all worked fine.

Comments Off on Dynamic Typing issues with Typescript/Angular 11

February 10th, 2021
6:54 pm
Pinning a title bar on a page with Angular 11/PrimeNG 11

Posted under Angular & PrimeNG & Uncategorized & Web
Tags ,

My initial attempt on this was to use static positioning when the title bar was unpinned (e.g. scrollable), and fixed positioning when the bar was pinned.

When using fixed positioning as above, the title bar is no longer in the normal flow so the rest of the content (in my case, encapsulated in a main container div) will sit underneath the title bar. I therefore used a top margin when pinned to prevent the top of the content being hidden. Some issues arose with this as follows:

1/ It was necessary therefore to add an additional margin to the main container to avoid occluding the content under the title bar, which meant exactly knowing its height. In addition, to allow a gap between the title bar and the content when pinned, I added a border to the title bar of the same colour as the page background, in effect to act as a kind of margin. This worked fine.

1/ My title bar was floated, so it automatically resized to fit its content. this meant that the margin allowance on the main content div would have to vary dynamically on resize.

2/ One way around this was to set media query breakpoints to trigger on the resize, and to set a fixed height for the title bar rather than floating it, and setting the margin on the content to match in each media query. In addition to this, I implemented a flexbox solution where the container was sized and the content centered within it. I was not so keen on this as it lost the ability to dynamically resize the title bar container as needed by the content resizing, and might cause problems if the layout/text changed in the title bar for example.

3/ Another way was to use a resize event and change the size of the margin on the content programmatically, using an rxjs debounced resize event. This worked pretty well, but I needed to switch the content margin on and off when the title bar was pinned/unpinned. This should have worked ok, but I noticed some slight visual movements in some page elements on pinning change, even though I could not see any style or layout changes in the browser inspector. This was annoying.

4/ I then tried using absolute positioning of the title bar when unpinned, and fixed positioning when pinned. This relied on using the same programmatic margin on the content for both, and in fact simplified the code, as I did not have to switch off the programmatic margin change when unpinning the title bar – it was used all the time, as absolute positioning was also outside the normal flow. Also, the slight glitches I saw on the page were completely eliminated.

I therefore stuck with method 4/ as the final solution. Some sample code showing the programmatic resize is as follows:-

private readonly resizeDebounceTime = 100;
private resizeObservable!: Observable<Event>;
private resizeSubscription!: Subscription;
private readonly formatPixelSize = (pixels: number) => `${pixels}px`;

ngAfterViewInit(): void {
   this.initBannerSpacingAdjustment();
}

ngOnDestroy(): void {
   this.destroyBannerSpacingAdjustment();
}

private initBannerSpacingAdjustment(): void {
   this.adjustBannerSpacing();
   this.resizeObservable = fromEvent(window, 
      ‘resize’).pipe(debounceTime(this.resizeDebounceTime));
   this.resizeSubscription = this.resizeObservable.subscribe(event => 
      this.adjustBannerSpacing());
}

private destroyBannerSpacingAdjustment(): void {
   if (this.resizeSubscription) {
      this.resizeSubscription.unsubscribe();
}
}

private adjustBannerSpacing(): void {
   /* note that the offsetHeight is the total height of the banner,
    * including padding and border */
   this.mainContainer.nativeElement.style.marginTop =
      this.formatPixelSize(this.banner.nativeElement.offsetHeight);
}

 

Comments Off on Pinning a title bar on a page with Angular 11/PrimeNG 11

February 8th, 2021
6:04 pm
Using Font Awesome with Angular 11/PrimeNG 11

Posted under Angular & PrimeNG & Uncategorized & Web
Tags ,

Update 4/3/21

I had problems again when trying to display font-awesome icons in a microapp/web component. In this case, my parent fabric application which loaded the web component had font awesome loaded in package.json but not in angular.json. I removed font-awesome from the web component application in both package.json and angular.json as the parent had loaded it in both places. This then worked correctly. There may be a similar issue with e.g. the prime icons etc. but have not researched this. Clearly PrimeNG itself is needed in both applications as the child web component has development dependencies on it. My theme styling however is only loaded in the parent fabric, and it makes sense that font-awesome behaves the same way.

Original Post

My previous post about using font awesome with Angular  up to v6 and PrimeNG up to v6 is here

With PrimeNG 11, Prime have introduced their own icon set, PrimeIcons.

I wanted to use some font awesome icons as well as they have a larger set, but initially they would not display. When trying again later it all worked fine, and am unsure what I got wrong the first time around. To be clear, the following steps were needed:

1/ Install font awesome and add to package.json (–save adds to package.json)

npm install font-awesome –save

2/ Add a style reference to angular.json

“styles”: [
“node_modules/primeng/resources/primeng.min.css”,
“node_modules/font-awesome/css/font-awesome.min.css”,
“node_modules/primeicons/primeicons.css”,
“src/messages.scss”,
“src/styles.scss”
],

Double check that the referenced css file actually exists under node-modules for the version in use. in my case, this was 4.7.0.

I was looking for pin/unpin icons for a banner bar, and could only find a vertical pin, fa-thumb-tack – I also wanted a horizontal pin to designate the unpinned state. Fortunately, font awesome has the ability to transform icons e.g. to rotate them. This is more limited in v4, but rotation is allowed, so my reference was as follows:

<p-toggleButton [(ngModel)]="bannerPinned" onIcon="fa fa-thumb-tack" offIcon="fa fa-thumb-tack fa-rotate-90"
class="ss-pin-banner"></p-toggleButton>

This worked perfectly. v5, which was not available directly to install via npm at the time, allows more transformations. The transforms are detailed in this stack overflow post and this font awesome page.

 

 

Comments Off on Using Font Awesome with Angular 11/PrimeNG 11

February 3rd, 2021
6:36 pm
404 errors on load when angular 11 app deployed to a domain subdirectory

Posted under Angular & Uncategorized & Web

As above, a webpack build in angular 11 which loaded and ran fine locally failed to load when deployed to a subdirectory of a domain hosted using cpanel.

This post here goes into the cause of the issue. The simple solution in my case was to change <base href=”/”> in index.html to <base href=”./”>. This then supports any subdirectory

In fact upon reviewing some past angular code, it appears that previous projects did include this change, but it was not clear if I had done this or if it was by default, and I could not remember.

This fix resolved the issue. Note that as per the post, there are build switches which allow changing of the href, but my change did not get overriden, and works fine for both development and prod situations, so I just left it in permanently.

Comments Off on 404 errors on load when angular 11 app deployed to a domain subdirectory

January 27th, 2021
6:34 pm
Creating and compiling a Custom PrimeNG 11 theme deployed as a static asset

Posted under Angular & PrimeNG & Uncategorized & Web
Tags ,

This follows on from this earlier post for Angular 6.

PrimeNG has for some time removed support for legacy themeroller themes and added their own architecture and theme editor. However, for the first time, the theme editor and the SCSS theming interface/standard SCSS files are no longer open source, but are a paid for option. The rest of the component set, including the delivered compiled css for all the standard themes, is available as open source as before.

Therefore, having considered the options, I elected to take a standard theme that was close to what I needed and edit its css directly. This is suggested as an option by Prime for small changes, but it is possible that later changes to the theme architecture could require theme changes as we are editing the compiled css. I decided that the risk and rework that might be needed was small, so have taken this route.

My policy on this is to copy the theme files to a new theme folder under assets/resources/themes/, and then to rename the theme.css file to theme.scss, as the compiled css is also valid scss. This then allows me to add my own variables as required for theme colours etc. to avoid duplication. The intention is to minimise alterations to the original theme, so I would only add variables where I had made changes, to minimise any rework if I had to take a new version of the standard theme and add my changes again.

Whilst doing this, I noted a warning that node-sass, as used previously, is now deprecated. I therefore switched to sass, as per this stackoverflow post, using npm uninstall -g node-sass followed by npm install -g sass. This worked fine, and my changes to the existing compilation script in package.json (also allowing multiple themes to be compiled) were as follows:-

 “scripts”: {
“ng”: “ng”,
“start”: “ng serve”,
“build”: “ng build”,
“test”: “ng test”,
“lint”: “ng lint”,
“e2e”: “ng e2e”,
“scss”: “npm run scss1 && npm run scss2”,
“scss1”: “cd src/assets/resources/themes/salient-new && sass theme.scss:theme.css –source-map .”
“scss2”: “cd src/assets/resources/themes/salient-new2 && sass theme.scss:theme.css –source-map .”
},

 

Comments Off on Creating and compiling a Custom PrimeNG 11 theme deployed as a static asset

January 13th, 2021
3:42 pm
404 error when loading external config script in angular 11

Posted under Angular & Uncategorized & Web
Tags ,

In angular 6 I was able to load custom static scripts, either via script tags in index.html, or dynamically, by placing the scripts in the project root (when using ng serve), or in the dist/projectname folder when running the built application. I originally used this for externalised configuration per my original post here, initially using script tags in index.html, and later using dynamic script loading via APP_INITIALIZER, which allowed dynamically named config files derived from query string parameters, and also early dynamic loading of external scripts such as for Yoti.

However, in angular 11, these use cases all gave 404 errors on the script. Posts such as this one here pointed to the need to place static files under the assets/ folder (although the post used an earlier angular version than 6, so I would expect it to work). I tried this, and both the static and dynamic use cases then worked correctly. I suspect that this is due to changes in the packaging when building and using ng serve. I retried a simple new project with a static script called from index.html, both under 6.1.7 and under 11.0.6. Under the older version, scripts in the root folder worked, but under 11.0.6, I had to place the scripts under assets (or in a subfolder of assets). This clearly demonstrated and resolved the problem.

Comments Off on 404 error when loading external config script in angular 11

January 5th, 2021
6:15 pm
Installing/running multiple Node/Angular/primeng + showcase versions under windows

Posted under Angular & PrimeNG & Web
Tags ,

I needed to upgrade node/angular/PrimeNG/primeNG Showcase to the latest versions (Node 15.5, Angular 11, PrimeNG 11) , but I also wanted to be able to maintain projects on older versions using Angular 6.

This post details how to do this with nvm under Linux, but an alternive nvm implementation is needed for windows. This is detailed here, and the git repo for this version with full instructions is here . I uninstalled the existing angular cli/PrimeNG via npm uninstall, and then removed my node installation via control panel.

Next I installed nvm per the above post for the windows implementation. Per the above post for the Linux version, I used nvm install to install node versions 10.4.0 and 15.5. I then selected each in turn via nvm use, and for each version, I installed the desired angular/cli for that version globally in the standard way, using for example npm install -g @angular/cli@6.2.2 for the legacy version. Note that using -g in this case installs globally for the currently selected node version only, so when you switch/select the current node version with npm use, you also automatically select the angular/cli version that you previously installed with it.

Having done this, and as per the above posts, you must remember to select the desired node version with nvm use in each command window or batch file that you use. The version is not selected for a project directory, but is per command window/process.

I was then able to use both the latest angular/primeNG and the legacy ones, and could build and run with both on the same machine.

One issue I hit with primeNG when performing npm install on the latest version was a bug in the latest version of node: “Cannot destructure property ‘name’ of ‘node’ as it is null.” This bug is detailed here. Per the post comment by Mika Bertels, I reverted to npm 6 using npm i -g npm@6. In my case, npm gave a file already exists error, so as hinted by node I used the –force flag to force the reversion of version. This worked fine, and my npm install of primeng worked successfully

Per the instructions here, the primeng 11 can just be installed and then run with ng serve, and I did not hit any issues with theme building etc. which I did with earlier versions, presumably due to all the themeing changes. Note also that this setup guide for primeng explains how to install primeng and primeicons when using with your own project – I did not need to do this initially as I was just running the showcase.

 

Comments Off on Installing/running multiple Node/Angular/primeng + showcase versions under windows