지시문을 포함하는 HTML을 수동으로 컴파일하고 싶습니다. $compile
Angular 2 와 동등한 기능은 무엇입니까 ?
예를 들어 Angular 1에서 HTML 조각을 동적으로 컴파일하고 DOM에 추가 할 수 있습니다.
var e = angular.element('<div directive></div>');
element.append(e);
$compile(e)($scope);
지시문을 포함하는 HTML을 수동으로 컴파일하고 싶습니다. $compile
Angular 2 와 동등한 기능은 무엇입니까 ?
예를 들어 Angular 1에서 HTML 조각을 동적으로 컴파일하고 DOM에 추가 할 수 있습니다.
var e = angular.element('<div directive></div>');
element.append(e);
$compile(e)($scope);
답변:
모든 세부 사항을 확인하려면 다음을 수행하십시오.
그것을 실제로 보려면 :
교장 :
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'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);
});
});
}
위 결과를 사용하는 코드 스 니펫
// 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;
//...
});
자세한 내용 은 여기를 읽 거나 작업 예를 참조하십시오.
.
.
OBSOLETE-Angular 2.0 RC5 관련 (RC5 전용)
이전 RC 버전에 대한 이전 솔루션을 보려면 이 게시물의 내역을 검색하십시오.
ComponentFactory
그리고 ViewContainerRef
지금은 사용되지 않는 DynamicComponentLoader을 대체 할 수 있습니다.
$compile($element.contents())($scope.$new());
이제 NgModule 생성이 완료된 수백 줄의 코드가 있습니다. 이것은 NG2를 피하고 더 나은 것으로 나아가고 자하는 종류의 것입니다.
참고 : @BennyBottema가 주석에서 언급했듯이 DynamicComponentLoader는 더 이상 사용되지 않으므로이 대답도 마찬가지입니다.
Angular2에는 $ compile에 해당하는 것이 없습니다 . DynamicComoponentLoader
ES6 클래스를 사용 하고 해킹하여 코드를 동적으로 컴파일 할 수 있습니다 (이 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'
);
}
}
그러나 html 파서가 angular2 코어 안에있을 때까지만 작동합니다.
DynamicComponentLoader
는 더 이상 사용되지 않으므로 Angular 2에서 동일한 종류의 작업을 어떻게 수행합니까? 모달 대화 상자가 있고 내용에 따라 새 구성 요소를 동적으로로드한다고 가정합니다.
내가 사용한 Angular 버전 -Angular 4.2.0
Angular 4는 런타임에 구성 요소 를로드하기 위해 ComponentFactoryResolver 와 함께 제공 됩니다. 이것은 Angular 1.0에서 $ compile 을 구현 한 것입니다.
이 아래 예제에서는 ImageWidget 구성 요소를 DashboardTileComponent에 동적으로 로드 합니다.
구성 요소를로드 하려면 동적 구성 요소를 배치하는 데 도움이되는 ng-template에 적용 할 수있는 지시문이 필요합니다.
WidgetHostDirective
import { Directive, ViewContainerRef } from '@angular/core';
@Directive({
selector: '[widget-host]',
})
export class DashboardTileWidgetHostDirective {
constructor(public viewContainerRef: ViewContainerRef) {
}
}
이 지시문은 ViewContainerRef 를 주입 하여 동적으로 추가 된 구성 요소를 호스팅 할 요소의 뷰 컨테이너에 액세스합니다.
DashboardTileComponent (동적 구성 요소를 렌더링하기위한 자리 표시 자 구성 요소)
이 구성 요소는 상위 구성 요소에서 오는 입력을 승인하거나 구현에 따라 서비스에서로드 할 수 있습니다. 이 구성 요소는 런타임시 구성 요소를 해결하기 위해 주요 역할을 수행합니다. 이 메소드에서는 궁극적으로 서비스에서 컴포넌트 이름을로드하고 ComponentFactoryResolver로 분석 하고 마지막으로 데이터를 동적 컴포넌트로 설정 하는 renderComponent () 메소드를 볼 수 있습니다 .
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;
}
entryComponents
. 그것 없이는 당신의 해결책이 작동하지 않을 것입니다
이 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>
컴포넌트의 인스턴스를 동적으로 생성하여 DOM에 연결하려면 다음 스크립트를 사용할 수 있으며 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);
}
}
한 번에 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>
$compile(...)($scope)
입니다. repo readme에도 아무것도 없습니다.
간단한 동적 각도 구성 요소를 컴파일 할 수있는 구성 요소를 볼 수 있습니다 https://www.npmjs.com/package/@codehint-ng/html-compiler
나는이 문제가 오래되었다는 것을 알고 있지만 AOT를 사용 하여이 작업을 수행하는 방법을 알아 내려고 몇 주를 보냈습니다. 객체를 컴파일 할 수 있었지만 기존 구성 요소를 실행할 수 없었습니다. 글쎄, 마침내 사용자 정의 템플릿을 실행하는 것만 큼 코드를 컴파일하지 않기 때문에 택트를 변경하기로 결정했습니다. 내 생각은 누구나 기존 공장을 통해 누구나 할 수있는 html을 추가하는 것이 었습니다. 그렇게하면서 요소 / 속성 / 등을 검색 할 수 있습니다. 해당 HTMLElement에서 구성 요소의 이름을 지정하고 실행합니다. 나는 그것을 작동시킬 수 있었고 내가 낭비하는 엄청난 시간을 다른 사람을 구하기 위해 이것을 공유해야한다고 생각했다.
@Component({
selector: "compile",
template: "",
inputs: ["html"]
})
export class CompileHtmlComponent implements OnDestroy {
constructor(
private content: ViewContainerRef,
private injector: Injector,
private ngModRef: NgModuleRef<any>
) { }
ngOnDestroy() {
this.DestroyComponents();
}
private _ComponentRefCollection: any[] = null;
private _Html: string;
get Html(): string {
return this._Html;
}
@Input("html") set Html(val: string) {
// recompile when the html value is set
this._Html = (val || "") + "";
this.TemplateHTMLCompile(this._Html);
}
private DestroyComponents() { // we need to remove the components we compiled
if (this._ComponentRefCollection) {
this._ComponentRefCollection.forEach((c) => {
c.destroy();
});
}
this._ComponentRefCollection = new Array();
}
private TemplateHTMLCompile(html) {
this.DestroyComponents();
this.content.element.nativeElement.innerHTML = html;
var ref = this.content.element.nativeElement;
var factories = (this.ngModRef.componentFactoryResolver as any)._factories;
// here we loop though the factories, find the element based on the selector
factories.forEach((comp: ComponentFactory<unknown>) => {
var list = ref.querySelectorAll(comp.selector);
list.forEach((item) => {
var parent = item.parentNode;
var next = item.nextSibling;
var ngContentNodes: any[][] = new Array(); // this is for the viewchild/viewchildren of this object
comp.ngContentSelectors.forEach((sel) => {
var ngContentList: any[] = new Array();
if (sel == "*") // all children;
{
item.childNodes.forEach((c) => {
ngContentList.push(c);
});
}
else {
var selList = item.querySelectorAll(sel);
selList.forEach((l) => {
ngContentList.push(l);
});
}
ngContentNodes.push(ngContentList);
});
// here is where we compile the factory based on the node we have
let component = comp.create(this.injector, ngContentNodes, item, this.ngModRef);
this._ComponentRefCollection.push(component); // save for our destroy call
// we need to move the newly compiled element, as it was appended to this components html
if (next) parent.insertBefore(component.location.nativeElement, next);
else parent.appendChild(component.location.nativeElement);
component.hostView.detectChanges(); // tell the component to detectchanges
});
});
}
}
HTML 코드를 사용하려면 지시문을 사용하십시오.
<div [innerHtml]="htmlVar"></div>
전체 구성 요소를 특정 위치에로드하려면 DynamicComponentLoader를 사용하십시오.
https://angular.io/docs/ts/latest/api/core/DynamicComponentLoader-class.html