Je veux compiler manuellement des directives contenant du HTML. Quel est l'équivalent de $compile
dans Angular 2?
Par exemple, dans Angular 1, je pourrais compiler de manière dynamique un fragment de HTML et l'ajouter au DOM:
var e = angular.element('<div directive></div>');
element.append(e);
$compile(e)($scope);
Pour obtenir tous les détails, vérifiez:
Pour voir cela en action:
Les directeurs:
1) Créer un modèle
2) Créer un composant
3) Créer un module
4) Module de compilation
5) Créer (et mettre en cache) ComponentFactory
6) utiliser Target pour en créer une instance
Un aperçu rapide de la création d'un composant
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;
}
Comment injecter un composant dans 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;
}
Un extrait de code indiquant comment créer une ComponentFactory
(et la mettre en cache)
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);
});
});
}
Un extrait de code sur la façon d'utiliser le résultat ci-dessus
// 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;
//...
});
La description complète avec tous les détails lisez ici , ou observez exemple de travail
.
.
OBSOLETE - Angular 2.0 RC5 (RC5 uniquement)
pour voir les solutions précédentes pour les versions précédentes de RC, veuillez rechercher à travers l'historique de ce message
Remarque: Comme @BennyBottema le mentionne dans un commentaire, DynamicComponentLoader est désormais obsolète, de même que cette réponse.
Angular2 n'a pas d'équivalent $ compile . Vous pouvez utiliser DynamicComoponentLoader
et pirater avec les classes ES6 pour compiler votre code dynamiquement (voir ceci 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'
);
}
}
Mais cela ne fonctionnera que jusqu’à ce que l’analyseur HTML soit à l’intérieur du noyau angular2.
Version angulaire que j'ai utilisée - Angular 4.2.0
Angular 4 est venu avec ComponentFactoryResolver pour charger des composants au moment de l'exécution. Ceci est une sorte de même implémentation de $ compile dans Angular 1.0 qui répond à vos besoins
Dans cet exemple ci-dessous, je charge ImageWidget composant de manière dynamique dans un DashboardTileComponent
Pour charger un composant, vous avez besoin d’une directive à laquelle vous pouvez appliquer ng-template , ce qui vous aidera à placer le composant dynamique.
WidgetHostDirective
import { Directive, ViewContainerRef } from '@angular/core';
@Directive({
selector: '[widget-Host]',
})
export class DashboardTileWidgetHostDirective {
constructor(public viewContainerRef: ViewContainerRef) {
}
}
cette directive injecte ViewContainerRef pour accéder au conteneur de vue de l'élément qui hébergera le composant ajouté dynamiquement.
DashboardTileComponent (composant titulaire titulaire pour rendre le composant dynamique)
Ce composant accepte une entrée provenant d'un composant parent ou vous pouvez charger à partir de votre service en fonction de votre implémentation. Ce composant joue le rôle principal pour résoudre les composants au moment de l'exécution. Dans cette méthode, vous pouvez également voir une méthode nommée renderComponent () qui charge finalement le nom du composant à partir d'un service et que vous résolvez avec ComponentFactoryResolver et enfin en définissant des données sur le composant dynamique.
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>
WidgetComponentService
Il s’agit d’une fabrique de services pour enregistrer tous les composants que vous souhaitez résoudre de manière dynamique.
import { Injectable } from '@angular/core';
import { ImageTextWidgetComponent } from "../templates/ImageTextWidget.Component";
@Injectable()
export class WidgetComponentService {
getComponent(componentName:string) {
if(componentName==="ImageTextWidgetComponent"){
return ImageTextWidgetComponent
}
}
}
ImageTextWidgetComponent (composant en cours de chargement à l'exécution)
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() { }
}
Ajouter Enfin, ajoutez cette ImageTextWidgetComponent dans votre module d'application en tant que 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;
}
ce paquet npm m'a facilité la tâche: https://www.npmjs.com/package/ngx-dynamic-template
usage:
<ng-template dynamic-template
[template]="'some value:{{param1}}, and some component <lazy-component></lazy-component>'"
[context]="{param1:'value1'}"
[extraModules]="[someDynamicModule]"></ng-template>
Fonctionne avec AOT + JIT en même temps.
J'ai créé comment l'utiliser ici: https://github.com/patrikx3/angular-compile
npm install p3x-angular-compile
Composant: Devrait avoir un contexte et des données HTML ...
Html:
<div [p3x-compile]="data" [p3x-compile-context]="ctx">loading ...</div>
Pour créer une instance d’un composant et l’attacher à votre DOM, vous pouvez utiliser le script suivant et travailler dans Angular RC:
modèle html:
<div>
<div id="container"></div>
<button (click)="viewMeteo()">Meteo</button>
<button (click)="viewStats()">Stats</button>
</div>
composant du chargeur
import { Component, DynamicComponentLoader, ElementRef, Injector } from '@angular/core';
import { WidgetMeteoComponent } from './widget-meteo';
import { WidgetStatComponent } from './widget-stat';
@Component({
moduleId: module.id,
selector: 'widget-loader',
templateUrl: 'widget-loader.html',
})
export class WidgetLoaderComponent {
constructor( elementRef: ElementRef,
public dcl:DynamicComponentLoader,
public injector: Injector) { }
viewMeteo() {
this.dcl.loadAsRoot(WidgetMeteoComponent, '#container', this.injector);
}
viewStats() {
this.dcl.loadAsRoot(WidgetStatComponent, '#container', this.injector);
}
}