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.