1

After updating Angular to version 17 with esbuild, a hash is added to the chunk name. The problem is that if you want to use lazy loading for Angular-elements, then in development mode a hash will be added for each chunk.

I cannot use the script directly in HTML, since the name changes after component update.

The question is how can I remove the hash from the names of chunks?

// before update
<script src="http://localhost:4200/src_components_test_test_component_ts.js" type="module"></script>

// after
<script src="http://localhost:4200/test-[hash].js" type="module"></script>

enter image description here

On stackblitz now default container with esbuild example

I try use NamedChunk options, but in code esbuilders config hash is part of naming EsBuilder naming

3
  • did you try - ng build --prod --named-chunks --output-hashing=none Commented May 3, 2024 at 4:30
  • Yes, but it dont work, because esbuild always create name chunk with hash chunkNames: options.namedChunks ? '[name]-[hash]' : 'chunk-[hash]',. You can see this in last link in question Commented May 3, 2024 at 4:47
  • Doesn't Angular lazy load these chunks if needed? Sounds like you are optimizing on your development build while in production, this is automatically done in a project I'm building. Commented May 3, 2024 at 9:49

1 Answer 1

1

I would not recommend dealing with the lazy loaded code inside the html.

Angular delivers options to load lazy components by rendering them into ng-templates in outlet refs.

Also removing output hashing is not recommended since it interferes with the accuracy of version deployment and browser cashing. You should only require the main.js of your angular elements.

Let me give you an example of dealing with lazy loaded code.

First we need a simple component, that has nothing but a outlet for the lazy component:


import { CommonModule } from '@angular/common';
import { Component, Input, OnInit, ViewChild, ViewContainerRef } from '@angular/core';

@Component({
  selector: 'app-lazy-wrapper',
  standalone: true,
  imports: [CommonModule],
  template: `<ng-template #outletRef></ng-template>`,
})
export class LazyWrapperComponent implements OnInit {
  // the place where the lazy component should be rendered
  @ViewChild('outletRef', { read: ViewContainerRef })
  public outletRef!: ViewContainerRef;

  // provide an input to allow the component to be selected
  @Input()
  public componentName = '';

  constructor(private injector: Injector) {}

  // define a lazy import map that points to a function that will lazily import a given component
  private lazyComponentMap: Record<string, (() => Promise<any>) | undefined> = {
    test: () =>
      import('../lazy-component/lazy-component.component').then(
        (module) => module.LazyComponentComponent
      ),
  };

  ngOnInit(): void {
    // on init the input of the component name should be picked here
    const lazyComp = this.lazyComponentMap[this.componentName];
    if (lazyComp) {
      // if a valid object exists, execute the import, and render the component in the outlet ref
      lazyComp().then((component) => {
        this.outletRef.createComponent(component, {
          // also use the injector so that the context is accurate
          injector: this.injector,
        });
      });
    }
  }
}

Now lets define a small component that we want to load lazy in angular elmenets.


import { Component } from '@angular/core';

@Component({
  selector: 'app-lazy-component',
  standalone: true,
  imports: [],
  template: `<h1 style="color:purple;"> I am a lazy component </h1>`,
})
export class LazyComponentComponent {
}


Then we import and define the lazywrapper as a custom element in our main.ts for angular elements:


import {createCustomElement} from "@angular/elements";
import { LazyWrapperComponent } from "../lazyStuff/lazy-wrapper/lazy-wrapper.component";
import { createApplication } from "@angular/platform-browser";

(async () => {
  const app = await createApplication({providers: [
    ]});

  const el = createCustomElement(LazyWrapperComponent, {
    injector: app.injector,
  })
  customElements.define("angular-lazy-wrapper", el);
})()

Now to the important part: Accessing the component and loading in the html of the hosts .html document.


<!DOCTYPE html>
<html lang="en" data-critters-container>
  <head>
    <title>My app</title>
    <meta charset="UTF-8">
  <body>
  <script src="polyfills.js" type="module"></script>
  <script src="main.js" type="module"></script>

  <!-- Important inputs in cusom elements are snake cased -->
  <angular-lazy-wrapper component-name="test"></angular-lazy-wrapper>
  <!-- we pass "test" as the accessor to the lazy import -->
</body>
</html>

No chunk import is required, only the main.js and maybe polyfills. This will now load the component lazily and render it.

A small implementation of this approach can be found here: https://stackblitz.com/edit/stackblitz-starters-btfhte?file=package.json

run: "npm run build:elements" "npm preview:elements"

And see the results.

For more references on ng-template and bindings as ViewContainerRef for lazy components read some documentation here: https://angular.io/api/core/ViewContainerRef#description

And also "createComponent" documentation: https://angular.io/api/core/createComponent

Sign up to request clarification or add additional context in comments.

4 Comments

Its work, if build index.html like angular, but if i want mount my web-component in other site or CMS like script?
I want to insert two web components into one page in different position and have them both connect to the same angular-core, but the result is different components.
It works also if you use it in a CMS. You simply have to make sure the cms loads the main.js. Then anywhere you want, you can insert the component with the custom element tag you defined as elaborated. I work on projects with multiple CMS and 3party vendors which use widgets as angular elements from our main app. You just need to define the CDN. Look in the stackblitz I provided. Otherwise please provide the buildsteps for how you create the elements and also the angular.json build step on how you build the entrypoint for angular elements in the first place
I reread it a couple of times and rethought it. Yes it suits my purposes. Thanks a lot!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.