Embed Angular components in React, Vue and even jQuery if you want

If you find a cool component in npm, but with the prefix ng, ngx, angular, and so on, then you should not be upset about this. There are many solutions to make this component available to you. In this article, we consider a solution that is officially supported by the Angular Team, namely, Angular Elements.

For practice, choose any component from Awesome Angular .

I chose a simple, but very interesting -  ngx-avatar . Which, in turn, displays avatars from various social networks, or simply displays the initials of the user.
Something like this:


And he has a simple Api, here’s a small example:

<ngx-avatarfacebookId="1508319875"></ngx-avatar><ngx-avatarsrc="assets/avatar.jpg"></ngx-avatar><ngx-avatarname="John Doe"></ngx-avatar>

And so, we will create for this Angular the project and we will connect library.

ng new avatar-lib --minimalN
npm i ngx-avatar --save

Connect the package to our module.

import { BrowserModule } from'@angular/platform-browser';
import { NgModule } from'@angular/core';
import { AppComponent } from'./app.component';
// Import your AvatarModuleimport { AvatarModule } from'ngx-avatar';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
// Specify AvatarModule as an import
AvatarModule
],
providers: [],
bootstrap: [AppComponent]
})
exportclassAppModule{ }

Now create a simple component, since by default `AvatarModule` does not export its only component to the outside.

ng g c avatar

import { Component, ViewEncapsulation, Input } from'@angular/core';
@Component({
  selector: 'app-avatar',
  template: `
    <ngx-avatar [name]="name"></ngx-avatar>
  `,
  styles: [],
  encapsulation: ViewEncapsulation.ShadowDom
})
exportclassAvatarComponent{
  @Input() name: string;
  constructor() { }
}

For simplicity, we used only one of the input parameters.

Create a universal component from all of this. In order for our elements to work, you need to add Angular Elements to our project.

ng add @angular/elements

Among other things, this command will also include in the scripts section a light version for registering Custom Elements.

"scripts": [{
     "input": "node_modules/document-register-element/build/document-register-element.js"
}]

The resulting app.module.ts:

import { BrowserModule } from'@angular/platform-browser';
import { NgModule, Injector } from'@angular/core';
import { createCustomElement } from'@angular/elements';
import { AvatarComponent } from'./avatar.component';
import { AvatarModule } from'ngx-avatar';
@NgModule({
declarations: [AvatarComponent],
  imports: [AvatarModule, BrowserModule],
  entryComponents: [AvatarComponent]
})
exportclassAppModule{
  constructor(private injector: Injector) {
    // Создаем наш кастомный Angular Elementconst avatarComponent = createCustomElement(AvatarComponent, {
      injector
    });
    // И обьявляем наш элемент
    customElements.define('avatar-lib', avatarComponent);
  }
  ngDoBootstrap() {}
}

Fine! Now we need to collect all this stuff. To do this, create your own collector, which will pack our component into one js file:

const fs = require('fs-extra');
const concat = require('concat');
(asyncfunctionbuild() {
  const files = [
    './dist/avatar-lib/runtime.js',
    './dist/avatar-lib/polyfills.js',
    './dist/avatar-lib/scripts.js',
    './dist/avatar-lib/main.js'
  ];
  await fs.ensureDir('elements');
  await concat(files, 'elements/avatar-lib.js');
})();

All is ready! it remains only to collect. Add this script to our package.json.

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

Well, that's it! It turned out we have such a js file `avatar-lib.js` weighing ~ 221kB and ~ 60kb in gzip. Naturally, many more are included with angular / core. The ngx-avatar itself weighs about 16.8kB and 5.4kB in gzip. In order to significantly reduce the weight of avatar-lib.js, we would need an Ivy Compiler, but this is a topic for another article. (I hope by this moment I will release Ivy, or I will collect it manually from what is available now).

What did Angular Elements give us?

This is just a convenient api for the implementation of Web Components. In fact, one could do without it. See what you could have done earlier: the article by Jia Li (now actively accompanies Zone.js) .

We implement our component in other frameworks.

First in Vue. We connect in any convenient way for you and insert into the template avatar-lib, data is passed through standard vue binging.

<avatar-lib :name = ‘myName’></avatar-lib>

You could also organize Output events.

Vue demo: https://github.com/Jamaks/angular-element/tree/master/ang-el-vue

It's time for React! Still as simple:

<avatar-lib name = {this.myName}></avatar-lib>

React demo

Well, in general, the project

As for browser support at the moment:

https://angular.io/guide/elements#browser-support-for-custom-elements
https://developer.mozilla.org/en- US / docs / Web / API / CustomElementRegistry / define

Of course, it remains for you to use this implementation method or not. I would like the UI components to have no connection to anything and have no similar suffixes: ng-, ngx-, v-, react-, rc- and so on.

Bonus!

Inspired by the size of the component (ngx-avatar), I tried all the same to feed ngtsc, ngcc and then build it using rollup. But the attempts were unsuccessful, since the selected component required a lot of external modules. In desperation, I made some similarity to this component, and the results were pleasantly surprised - at the moment (7.1.2) the library turned out ~ 96kb and ~ 26kb in gzip. Naturally, there were many dependencies there, and my rollup configuration leaves much to be desired. But still this is not 3kb, which we showed at the presentation of Ivy. It remains to wait when ngcc is implemented in the Webpack (cli) and the documentation is written.

Also small experiments and insiders from the world of Angular can be found here .

Also popular now: