Using Angular 2 components in static & non-SPA web

2020-04-10 03:52发布

问题:

I would like to know if it is possible to use Angular 2 components built using Angular CLI in non-SPA apps. I would like to develop some independent widget like components & bootstrap those component in my static website or already existing non-angular web application.

Is there any way to use generated build bundle in Angular CLI to include in static HTML pages & bootstrap the components as per the requirement.

Or what is the suggested way of doing such implementations, i.e. build Angular 2 components & use those in non-SPA apps.

回答1:

I had been trying to figure this out also and finally found the answer if you don't want Angular to absolutely control the entire frontend you can use Angular Elements. These are components that have been exported to run outside an Angular app but still supports attributes and other Angular functionality.

I found a couple of articles online that explain it in a little more detail.

https://nitayneeman.com/posts/a-practical-guide-to-angular-elements/ https://blog.angulartraining.com/tutorial-how-to-create-custom-angular-elements-55aea29d80c5 https://angular.io/guide/elements#example-a-popup-service

When I tried the samples I ran into a couple of errors about createCustomElement not being able to add them. This was probably due to the browser I was using for debugging (Edge) due to not supporting custom elements so I ended up having to install @webcomponents/custom-elements

npm install @webcomponents/custom-elements

Then everything ran fine. Basically, the bottom line is you create your app as usual but instead of bootstrapping the AppModule you create a custom ngDoBootstrap method to initialize and install the components as custom elements. Note: You need to add the components in the entryComponents array in the app module.

app.module.ts

import { BrowserModule } from '@angular/platform-browser';
import { NgModule, Injector } from '@angular/core';
import { createCustomElement } from '@angular/elements';

import { AppComponent } from './app.component';
import { TestComponent } from './test/test.component';

@NgModule({
   declarations: [
      AppComponent,
      TestComponent
   ],
   entryComponents: [
      TestComponent
   ],
   imports: [
      BrowserModule
   ],
   providers: [],
   // bootstrap: [AppComponent]
})
export class AppModule {

   constructor(private injector: Injector) {
   }

   ngDoBootstrap() {
      const customElement = createCustomElement(TestComponent, { injector: this.injector });
      customElements.define('app-test', customElement);
   }
}

test.component.ts

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

@Component({
    selector: 'app-test',
    template: '<div style="border: 1px solid red">Test Component Works!</div>',
    styleUrls: ['./test.scss']
})
/** test component*/
export class TestComponent {
    /** test ctor */
    constructor() {

    }
}

Then in order for cross-browser support issues, you also need to update the polyfills.ts and add the following at the end.

// Used for browsers with partially native support of Custom Elements
import '@webcomponents/custom-elements/src/native-shim';

// Used for browsers without a native support of Custom Elements
import '@webcomponents/custom-elements/custom-elements.min';

If you look at the samples above they mention using a custom build script to combine all the files into one file but you can also simply load the individual files also.

Index.html

<!doctype html>
<html>
<head>
    <base href="/">
</head>
<body>
    Testing Component
    <app-test></app-test>

    <script type="text/javascript" src="~/js/runtime.js"></script>
    <script type="text/javascript" src="~/js/es2015-polyfills.js"></script>
    <script type="text/javascript" src="~/js/polyfills.js"></script>
    <script type="text/javascript" src="~/js/styles.js"></script>
    <script type="text/javascript" src="~/js/scripts.js"></script>
    <script type="text/javascript" src="~/js/vendor.js"></script>
    <script type="text/javascript" src="~/js/main.js"></script>

</body>
</html>

Note: In order for my scripts to be output into the wwwroot/js folder I updated the angular.json

"architect": {
    "build": {
      "builder": "@angular-devkit/build-angular:browser",
      "options": {
        "outputPath": "../wwwroot/js",
        ...
      }
   }
},

then just used the normal command ng build to generate the scripts. You can also use ng build --watch to keep the scripts updated everytime there is a change made.



回答2:

I don't think this would work, as if you don't serve the Angular files I'm not certain how the browser would understand about what you're talking about at all.

You could leverage Angular components in other Angular applications, but I don't think you could use them in non-angular/non-SPA situations, unless you served the whole core package along with it and embedded it somewhere on the page.



回答3:

@chrispy is right. When you build your angular application, you'll have an index.html files along with some .js, .css and other assets. You have to include all these files (especially javascript files) and make them available on your server in order to make your components to work.

If you want just a part of your application to use javascript, why not use jQuery or plain javascript ?



回答4:

You can use/reuse components but you need separate bootstrap for each widget which points to application tag, because in fact you'll have multiple angular apps running on the same page.