DEV Community

loading...
Cover image for The Angular (Elements) gift to Web

The Angular (Elements) gift to Web

bergermarko profile image Marko Berger ・3 min read

Intro in Angular Elements

Imagine that your client wants to sell her or his service on the third-party web app. You then have to somehow integrate your service on their application or redirect to your application. That is not an easy job to and user experience is awful. So how does Angular Elements helps us with that? Did you hear about Web Component Technology?

Web components are a set of web platform APIs that allow you to create new custom, reusable, encapsulated HTML tags to use in web pages and web apps.

This tech was introduced in 2011 by Alex Russell at Fronteers Conference.
And now Angular found the way to introduce this tech into his own environment.
Let’s see what documentation says.

Angular elements are Angular components packaged as custom elements, a web standard for defining new HTML elements in a framework-agnostic way.

Cool right!

Enough talk lets code.

We will create element registration-form

npm install -g @angular/cli 
ng new registration-form
ng add @angular/elements project=registration-form

@angular/elements are a package that contains all the good stuff we need for creating Angular elements. It contains document-register-element a lightweight version of the W3C Custom Elements specification. Remember Angular Elements are still young. So there are still some issues. One of them is that you need to change the document-register-element version from 1.7.2 to 1.8.1 (change it in package.json and do npm install).

All the preps are done. Let's make our hands dirty.

First, generate the new component.

ng g c register

registration.component.ts

@Component({
  selector: 'register',
  templateUrl: './register.component.html',
  styleUrls: ['./register.component.css'],
  encapsulation: ViewEncapsulation.Native
})

What is going on here?

We want to have isolated application inside of other application with our own CSS style. See the problem. To solve this. We are got to tell Angular to use Shadow Dom (encapsulation part of the code) to isolate our CSS styles and compile it into JavaScript. So that we can bundle it into one file. There are other ViewEncapsulation strategies but Native is one preferred.
Now you can do that nice Angular magic we all love inside component.ts, component.html, and component.css component files.
But if you want to use the same CSS styles of the "parent" page. Just switch encapsulation to None and remove styleUrls.
I will skip the part of developing registration form and fast forward to the place where the real the magic happens the app.module.ts.

@NgModule({
  declarations: [
    RegisterComponent
  ],
  imports: [
    BrowserModule,
    FormsModule,
    HttpClientModule
  ],
  entryComponents: [
    RegisterComponent
  ],
  providers: [],
  bootstrap: []
})
export class AppModule {
  constructor(private injector: Injector) {

  }
  ngDoBootstrap() {
    const el = createCustomElement(RegisterComponent, {injector: this.injector});
    customElements.define('register', el);
  }
 }

First, we need to add our RegisterFormComponent to entryComponents. Because our component is defined but not declared in our application. The second task is to implement manual bootstrapping with ngDoBootstrap. The third and final task is to convert an Angular component to native DOM API with createCustomElement().

That's that.

The only thing we need is to compile, bundle and test our Custom Element.
Remove all app.componet files. We don't need them.
Note: don’t remove app.module.ts.

We are not finished yet

Now, this is something we all wish that will be updated in next versions of Angular-CLI and you will see why.

If you do ng build --prod it will create a bunch of files with hash strings in there name. We don't want this.

So we need to do a little hack.

npm install fs-extra concat --save-dev

After the installation has finished, go to the project root folder and create a build-script.js file.

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

(async function build() {
  const files = [
    './dist/register/runtime.js',
    './dist/register/polyfills.js',
    './dist/register/scripts.js',
    './dist/register/main.js'
  ];

  await fs.ensureDir('elements');
  await concat(files, 'elements/register-form.js');
})();

Note: This script is for Angular 7. If you are using Angular 6 you need to adjust paths in dist/ folder.
This script will bundle all those files into one register-form.js file for us to use and put in elements directory.

Next step let’s add our new build script to package.json.

"scripts": {
    "ng": "ng",
    "start": "ng serve",
    "build": "ng build",
    "build:elements": "ng build --prod --output-hashing none && node build-elements.js",
    "test": "ng test",
    "lint": "ng lint",
    "e2e": "ng e2e"
  },

Run the script

ng build:elements

Test it

Create basic index.html file in the root directory and just add .

ng serve 

and viola.

Discussion (0)

Forem Open with the Forem app