Vyhledávání na webu

typescript - Ekvivalent $compile v Angular 2


Chci ručně kompilovat nějaký HTML obsahující direktivy. Jaký je ekvivalent $compile v Angular 2?

Například v Angularu 1 jsem mohl dynamicky zkompilovat fragment HTML a přidat ho do DOM:

var e = angular.element('<div directive></div>');
element.append(e);
$compile(e)($scope);

Odpověď č. 1

Angular 2.3.0 (07.12.2016)

Pro všechny podrobnosti zkontrolujte:

  • Jak mohu použít/vytvořit dynamickou šablonu pro kompilaci dynamické komponenty v Angular 2.0?

Abychom to viděli v akci:

  • pozorovat fungující plunker (fungující s 2.3.0+)

Principálové:

1) Vytvořit šablonu
2) Vytvořit komponentu
3) Vytvořit modul
4) Zkompilovat modul
5) Vytvořit (a uložit do mezipaměti) ComponentFactory
6) Použít cílový prvek k vytvoření jeho instance

Rychlý přehled o tom, jak vytvořit komponentu

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

Způsob, jak vložit komponentu do 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;
}

Ukázka kódu, jak vytvořit ComponentFactory (a uložit ji do mezipaměti)

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

Ukázka kódu, jak použít výše uvedený výsledek

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

Plný popis se všemi detaily si přečtěte zde, nebo si prohlédněte funkční příklad

.

.

ZASTARALÉ - související s Angular 2.0 RC5 (pouze RC5)

chcete-li vidět předchozí řešení pro předchozí verze RC, prosím, prohledejte historii tohoto příspěvku

Odpověď č. 2

Poznámka: Jak @BennyBottema zmiňuje v komentáři, DynamicComponentLoader je nyní zastaralý, a tudíž je zastaralá i tato odpověď.

Angular2 nemá žádný ekvivalent k $compile. Můžete použít DynamicComponentLoader a pomocí tříd ES6 hackovat, abyste svůj kód kompilovali dynamicky (viz tento 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'
    );
  }
}

Ale bude to fungovat jen do té doby, než bude HTML parser součástí jádra Angular2.

Odpověď č. 3

Verze Angular, kterou jsem použil - Angular 4.2.0

Angular 4 přišel s ComponentFactoryResolver pro načítání komponent za běhu. Jedná se o obdobnou implementaci $compile v Angular 1.0, která slouží vašim potřebám.

V níže uvedeném příkladu dynamicky načítám komponentu ImageWidget do DashboardTileComponent

Aby bylo možné načíst komponentu, potřebujete direktivu, kterou můžete použít na ng-template, která pomůže umístit dynamickou komponentu.

SměrniceWidgetHost

 import { Directive, ViewContainerRef } from '@angular/core';

    @Directive({
      selector: '[widget-host]',
    })
    export class DashboardTileWidgetHostDirective {
      constructor(public viewContainerRef: ViewContainerRef) { 


      }
    }

Tato direktiva vkládá ViewContainerRef, aby získala přístup k kontejneru zobrazení prvku, který bude hostit dynamicky přidanou komponentu.

DashboardTileComponent (komponenta zástupce pro vykreslení dynamické komponenty)

Tato komponenta přijímá vstup, který pochází od nadřazených komponent, nebo jej můžete načíst ze své služby na základě vaší implementace. Tato komponenta hraje hlavní roli při řešení komponent za běhu. V této metodě také můžete vidět metodu pojmenovanou renderComponent(), která nakonec načítá název komponenty ze služby a řeší jej pomocí ComponentFactoryResolver a nakonec nastavuje data do dynamické komponenty.

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>

WidgetKomponentaSlužba

Toto je továrna služeb pro registraci všech komponent, které chcete řešit dynamicky

import { Injectable }           from '@angular/core';
import { ImageTextWidgetComponent } from "../templates/ImageTextWidget.Component";
@Injectable()
export class WidgetComponentService {
  getComponent(componentName:string) {
          if(componentName==="ImageTextWidgetComponent"){
              return ImageTextWidgetComponent
          }
  }
}

ImageTextWidgetComponent (komponenta, kterou načítáme za běhu)

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() { }
}

Nakonec přidejte tento ImageTextWidgetComponent do vašeho aplikačního modulu jako entryComponent

@NgModule({
    imports: [BrowserModule],
    providers: [WidgetComponentService],
    declarations: [
        MainApplicationComponent,
        DashboardHostComponent,
        DashboardGroupComponent,
        DashboardTileComponent,
        DashboardTileWidgetHostDirective,
        ImageTextWidgetComponent
        ],
    exports: [],
    entryComponents: [ImageTextWidgetComponent],
    bootstrap: [MainApplicationComponent]
})
export class DashboardModule {
    constructor() {

    }
}

ModelDlaždice

 export interface TileModel {
      data: any;
    }

Původní odkaz z mého blogu

Oficiální dokumentace

Stáhnout ukázkový zdrojový kód

Související články