typescript - Motsvarigheten till $compile i Angular 2
Jag vill manuellt kompilera några HTML-direktiv som innehåller. Vad motsvarar $compile i Angular 2?
Till exempel, i Angular 1, kunde jag dynamiskt kompilera ett fragment av HTML och lägga till det i DOM:
var e = angular.element('<div directive></div>');
element.append(e);
$compile(e)($scope);
Svar #1
Angular 2.3.0 (2016-12-07)
För att få alla detaljer, kolla:
- Hur kan jag använda/skapa en dynamisk mall för att kompilera en dynamisk komponent med Angular 2.0?
För att se det i praktiken:
- observera en fungerande plunker (fungerar med 2.3.0+)
Principalerna:
1) Skapa mall
2) Skapa komponent
3) Skapa modul
4) Kompilera modul
5) Skapa (och cache) ComponentFactory
6) Använd Target för att skapa en instans av den
En snabb översikt över hur man skapar en komponent
createNewComponent (tmpl:string) {
@Component({
selector: 'dynamic-component',
template: tmpl,
})
class CustomDynamicComponent implements IHaveDynamicData {
@Input() public entity: any;
};
// a component for this particular template
return CustomDynamicComponent;
}
Ett sätt att injicera en komponent i NgModule
createComponentModule (componentType: any) {
@NgModule({
imports: [
PartsModule, // there are 'text-editor', 'string-editor'...
],
declarations: [
componentType
],
})
class RuntimeComponentModule
{
}
// a module for just this Type
return RuntimeComponentModule;
}
Ett kodexempel på hur man skapar en ComponentFactory (och cachar den)
public createComponentFactory(template: string)
: Promise<ComponentFactory<IHaveDynamicData>> {
let factory = this._cacheOfFactories[template];
if (factory) {
console.log("Module and Type are returned from cache")
return new Promise((resolve) => {
resolve(factory);
});
}
// unknown template ... let's create a Type for it
let type = this.createNewComponent(template);
let module = this.createComponentModule(type);
return new Promise((resolve) => {
this.compiler
.compileModuleAndAllComponentsAsync(module)
.then((moduleWithFactories) =>
{
factory = _.find(moduleWithFactories.componentFactories
, { componentType: type });
this._cacheOfFactories[template] = factory;
resolve(factory);
});
});
}
Ett kodexempel på hur man använder ovanstående resultat
// here we get Factory (just compiled or from cache)
this.typeBuilder
.createComponentFactory(template)
.then((factory: ComponentFactory<IHaveDynamicData>) =>
{
// Target will instantiate and inject component (we'll keep reference to it)
this.componentRef = this
.dynamicComponentTarget
.createComponent(factory);
// let's inject @Inputs to component instance
let component = this.componentRef.instance;
component.entity = this.entity;
//...
});
Den fullständiga beskrivningen med alla detaljer läs här, eller observera fungerande exempel
.
.
UTDATERRAD - Angular 2.0 RC5-relaterad (endast RC5)
för att se tidigare lösningar för tidigare RC-versioner, vänligen, sök igenom historiken av detta inlägg
Svar #2
Obs: Som @BennyBottema nämner i en kommentar, är DynamicComponentLoader nu föråldrad, och det gäller därför också detta svar.
Angular2 har ingen motsvarighet till $compile. Du kan använda DynamicComoponentLoader och hacka med ES6-klasser för att kompilera din kod dynamiskt (se denna plunk):
import {Component, DynamicComponentLoader, ElementRef, OnInit} from 'angular2/core'
function compileToComponent(template, directives) {
@Component({
selector: 'fake',
template , directives
})
class FakeComponent {};
return FakeComponent;
}
@Component({
selector: 'hello',
template: '<h1>Hello, Angular!</h1>'
})
class Hello {}
@Component({
selector: 'my-app',
template: '<div #container></div>',
})
export class App implements OnInit {
constructor(
private loader: DynamicComponentLoader,
private elementRef: ElementRef,
) {}
ngOnInit() {} {
const someDynamicHtml = `<hello></hello><h2>${Date.now()}</h2>`;
this.loader.loadIntoLocation(
compileToComponent(someDynamicHtml, [Hello])
this.elementRef,
'container'
);
}
}
Men det kommer bara att fungera så länge html-parsern är inuti Angular2-kärnan.
Svar #3
Angular version jag har använt - Angular 4.2.0
Angular 4 har kommit på ComponentFactoryResolver för att ladda komponenter vid körning. Detta är ungefär samma implementation av $compile i Angular 1.0 som tillgodoser dina behov
I exemplet nedan laddar jag ImageWidget-komponenten dynamiskt i en DashboardTileComponent
För att ladda en komponent behöver du en direktiv som du kan applicera på ng-template som hjälper till att placera den dynamiska komponenten
WidgetHostDirektiv
import { Directive, ViewContainerRef } from '@angular/core';
@Directive({
selector: '[widget-host]',
})
export class DashboardTileWidgetHostDirective {
constructor(public viewContainerRef: ViewContainerRef) {
}
}
denna direktiv injicerar ViewContainerRef för att få åtkomst till vybehållaren för elementet som kommer att hysa den dynamiskt tillagda komponenten.
DashboardTileComponent(Platshållarkomponent för att rendera den dynamiska komponenten)
Denna komponent accepterar ett input som kommer från en föräldrakomponent eller så kan du ladda från din tjänst baserat på din implementation. Denna komponent spelar huvudrollen för att lösa komponenterna vid körning. I denna metod kan du också se en metod som heter renderComponent() som slutligen laddar komponentnamnet från en tjänst och löser det med ComponentFactoryResolver och slutligen sätter data till den dynamiska komponenten.
import { Component, Input, OnInit, AfterViewInit, ViewChild, ComponentFactoryResolver, OnDestroy } from '@angular/core';
import { DashboardTileWidgetHostDirective } from './DashbardWidgetHost.Directive';
import { TileModel } from './Tile.Model';
import { WidgetComponentService } from "./WidgetComponent.Service";
@Component({
selector: 'dashboard-tile',
templateUrl: 'app/tile/DashboardTile.Template.html'
})
export class DashboardTileComponent implements OnInit {
@Input() tile: any;
@ViewChild(DashboardTileWidgetHostDirective) widgetHost: DashboardTileWidgetHostDirective;
constructor(private _componentFactoryResolver: ComponentFactoryResolver,private widgetComponentService:WidgetComponentService) {
}
ngOnInit() {
}
ngAfterViewInit() {
this.renderComponents();
}
renderComponents() {
let component=this.widgetComponentService.getComponent(this.tile.componentName);
let componentFactory = this._componentFactoryResolver.resolveComponentFactory(component);
let viewContainerRef = this.widgetHost.viewContainerRef;
let componentRef = viewContainerRef.createComponent(componentFactory);
(<TileModel>componentRef.instance).data = this.tile;
}
}
DashboardTileComponent.html
<div class="col-md-2 col-lg-2 col-sm-2 col-default-margin col-default">
<ng-template widget-host></ng-template>
</div>
WidgetKomponentTjänst
Detta är en tjänstfabrik för att registrera alla komponenter som du vill lösa dynamiskt
import { Injectable } from '@angular/core';
import { ImageTextWidgetComponent } from "../templates/ImageTextWidget.Component";
@Injectable()
export class WidgetComponentService {
getComponent(componentName:string) {
if(componentName==="ImageTextWidgetComponent"){
return ImageTextWidgetComponent
}
}
}
ImageTextWidgetComponent (komponent som vi laddar vid körning)
import { Component, OnInit, Input } from '@angular/core';
@Component({
selector: 'dashboard-imagetextwidget',
templateUrl: 'app/templates/ImageTextWidget.html'
})
export class ImageTextWidgetComponent implements OnInit {
@Input() data: any;
constructor() { }
ngOnInit() { }
}
Lägg slutligen till denna ImageTextWidgetComponent i din appmodul som entryComponent
@NgModule({
imports: [BrowserModule],
providers: [WidgetComponentService],
declarations: [
MainApplicationComponent,
DashboardHostComponent,
DashboardGroupComponent,
DashboardTileComponent,
DashboardTileWidgetHostDirective,
ImageTextWidgetComponent
],
exports: [],
entryComponents: [ImageTextWidgetComponent],
bootstrap: [MainApplicationComponent]
})
export class DashboardModule {
constructor() {
}
}
TileModel
export interface TileModel {
data: any;
}
Originalreferens från min blogg
Officiell dokumentation
Ladda ner exempel på källkod
