Эквивалент компиляции $в Angular 2

Я хочу вручную скомпилировать некоторые HTML-содержащие директивы. Что эквивалентно $compile в Angular 2?

Например, в Angular 1 я мог бы динамически скомпилировать фрагмент HTML и добавить его в DOM:

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

Ответ 1

Angular 2.3.0 (2016-12-07)

Чтобы узнать все подробности, проверьте:

Чтобы увидеть это в действии:

Принципы:

1) Создать шаблон
2) Создать компонент
3) Создать модуль
4) Модуль компиляции
5) Создать (и кэшировать) ComponentFactory
6) использовать Target для создания его экземпляра

Краткий обзор того, как создать компонент

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

Способ внедрения компонента в 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;
}

Фрагмент кода, как создать ComponentFactory (и кэшировать его)

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

Фрагмент кода, как использовать приведенный выше результат

  // 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 inject @Inputs to component instance
        let component = this.componentRef.instance;

        component.entity = this.entity;
        //...
    });

Полное описание со всеми подробностями читайте здесь или ознакомьтесь с рабочим примером

.

.

OBSOLETE - Angular 2.0 RC5 related (RC5 only)

чтобы увидеть предыдущие решения для предыдущих версий RC, пожалуйста, просмотрите историю этого поста

Ответ 2

Примечание: Как отмечает @BennyBottema в комментарии, DynamicComponentLoader теперь устарел, следовательно, так и этот ответ.


Angular2 не имеет эквивалента $compile. Вы можете использовать DynamicComoponentLoader и взломать классы ES6 для динамического компиляции кода (см. Этот бухнуться):

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

Но он будет работать только до тех пор, пока парсер html не окажется внутри ядра angular2.

Ответ 3

Угловая версия, которую я использовал - Angular 4.2.0

Angular 4 предлагает ComponentFactoryResolver для загрузки компонентов во время выполнения. Это своего рода реализация $ compile в Angular 1.0, которая отвечает вашим потребностям

В этом примере ниже я загружаю компонент ImageWidget динамически в DashboardTileComponent

Для загрузки компонента вам нужна директива, которую вы можете применить к ng-template , которая поможет разместить динамический компонент

WidgetHostDirective

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

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


      }
    }

эта директива внедряет ViewContainerRef, чтобы получить доступ к контейнеру представления элемента, в котором будет размещен динамически добавленный компонент.

DashboardTileComponent(Поместить компонент-держатель для рендеринга динамического компонента)

Этот компонент принимает входные данные, поступающие от родительских компонентов, или вы можете загрузить их из службы на основе вашей реализации. Этот компонент выполняет основную роль для разрешения компонентов во время выполнения. В этом методе вы также можете увидеть метод с именем renderComponent(), который в конечном итоге загружает имя компонента из службы и разрешает его с помощью ComponentFactoryResolver и, наконец, устанавливает данные для динамического компонента.

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

Это фабрика сервисов для регистрации всех компонентов, которые вы хотите динамически разрешать

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

ImageTextWidgetComponent(компонент, который мы загружаем во время выполнения)

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

Добавить Наконец добавьте этот ImageTextWidgetComponent в свой модуль приложения как 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;
    }

Оригинальная ссылка из моего блога

Официальная документация

Скачать образец исходного кода

Ответ 4

этот пакет npm облегчил мне задачу: https://www.npmjs.com/package/ngx-dynamic-template

Использование:

<ng-template dynamic-template
             [template]="'some value:{{param1}}, and some component <lazy-component></lazy-component>'"
             [context]="{param1:'value1'}"
             [extraModules]="[someDynamicModule]"></ng-template>

Ответ 5

Чтобы динамически создать экземпляр компонента и прикрепить его к DOM, вы можете использовать следующий script и должны работать в Angular RC:

html-шаблон:

<div>
  <div id="container"></div>
  <button (click)="viewMeteo()">Meteo</button>
  <button (click)="viewStats()">Stats</button>
</div>

Компонент загрузчика

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

}

Ответ 6

Angular TypeScript/ES6 (Angular 2 +)

Работает с AOT + JIT одновременно вместе.

Я создал, как использовать его здесь: https://github.com/patrikx3/angular-compile

npm install p3x-angular-compile

Компонент: должен иметь контекст и некоторые html-данные...

Html:

<div [p3x-compile]="data" [p3x-compile-context]="ctx">loading ...</div>

Ответ 8

Если вы хотите ввести директиву использования html-кода

<div [innerHtml]="htmlVar"></div>

Если вы хотите загрузить весь компонент в определенном месте, используйте DynamicComponentLoader:

https://angular.io/docs/ts/latest/api/core/DynamicComponentLoader-class.html