web-dev-qa-db-fra.com

Équivalent de $ compile in Angular 2

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);
124
pixelbits

Angular 2.3. (2016-12-07)

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

126
Radim Köhler

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.

33
alexpods

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;
    }

référence originale de mon blog

Documentation officielle

Télécharger l'exemple de code source

15
Code-EZ

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>
8
aelbatal

Angular TypeScript/ES6 (Angular 2+)

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>
3
Patrik Laszlo

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);
  }

}
3
fabio_biondi