Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Build angular element from existing project

We have an existing Angular 8 project which includes a bunch of common components (like custom datepickers, numeric inputs, ...). The project itself is a standard Angular-app.

We want to extract some of these components as Angular elements that they can be used in other Angular apps or even other projects which are not Angular related.

Is it possible to build the project in a way that only the code specific to a single Angular element is included in the generated JavaScript files?

What I need is something like this: ng build --element myDatePicker

This should generate all the JavaScript files for the myDatePicker-Element only with all the needed dependencies but without any other code from the project (like other components, modules, ...).

The whole app should still normally build when I use ng build.

like image 567
Stefan Avatar asked Aug 05 '19 08:08

Stefan


1 Answers

I suggest you to take a look at Angular Elements, more specifically createCustomElement. This will help you achieve exactly what you need.

I can give you a short guide on what you could do.

My Github Repo for example

1) First thing is to install @angular/elements

npm i @angular/elements --save

2) This step includes modifying your app.module.ts. If you want to build out only a set of chosen components (custom date-pickers, numeric inputs, etc...) you will have to remove the bootstrap: [ ] for a moment and replace it with entryComponents: [ ]. In entryComponents you will put the custom components you want to build.

entryComponents: [MyCustomDatePickerComponent, CustomButtonComponent]

3) Now it's time to tell the Angular to bootstrap your custom components. You will also have to define the selector for your custom components. You will call them with that selector from any other app where you inject them. Ex: <my-button-element></my-button-element>

export class AppModule {

  constructor(private injector: Injector) {}

  ngDoBootstrap() {
    const el = createCustomElement(CustomButtonComponent,
      { injector: this.injector });
    customElements.define('my-button-element', el);
    const el2 = createCustomElement(MyCustomDatePickerComponent,
      { injector: this.injector });
    customElements.define('custom-date-picker', el2);
  }
}

4) Building the components - If you run ng build --prod you will get multiple files. We want to merge those files in one to allow easier use and injection of custom elements we've built. There are two packages that can help us do that. npm install --save-dev concat fs-extra

Create an elements-build.js in root of your application and paste this. (It will merge the build output in one .js and .css file and put them in root/elements folder.

const concat = require('concat');
const fs = require('fs-extra');

(async function build() {
  // Instead of /CustomElements/ put your project name - just check a dist folder to see how its formatted and make it that way
  const files = [
    './dist/CustomElements/runtime-es2015.js',
    './dist/CustomElements/polyfills-es2015.js',
    './dist/CustomElements/main-es2015.js'
  ];

  await fs.ensureDir('elements');
  await concat(files, 'elements/myCustomElements.js');
  await fs.copyFile(
    './dist/CustomElements/styles.css',
    'elements/styles.css'
  );
})();

5) In your package.json add

"build:elements": "ng build --prod --output-hashing none && node elements-build.js",

6) That's it. You've build your custom elements and can reuse them anywhere in other Angular apps or even in non Angular apps. The only thing you have to do is to import the .js and .css you just created. You can even use the Input() & Output() as you would normally.

<custom-date-picker dateTitle="My Date Title"></custom-date-picker>

NOTE: If you decide to import it to another Angular application, you might have problems with zone.js as it will be imported twice. The solution is to import it only once (whether from core app, or an custom element).

like image 102
Dino Avatar answered Oct 01 '22 22:10

Dino