Эквивалент компиляции $в 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>
Ответ 7
Angular v5.0.2 (19/11/2017)
Angular документально подтвердил эту версию для текущей версии:
https://angular.io/guide/dynamic-component-loader
Ответ 8
Если вы хотите ввести директиву использования html-кода
<div [innerHtml]="htmlVar"></div>
Если вы хотите загрузить весь компонент в определенном месте, используйте DynamicComponentLoader:
https://angular.io/docs/ts/latest/api/core/DynamicComponentLoader-class.html