Archive for February, 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