Tìm kiếm trang web

typescript - Tương đương với $compile trong Angular 2


Tôi muốn biên dịch thủ công một số HTML chứa các chỉ thị. Tương đương với $compile trong Angular 2 là gì?

Ví dụ, trong Angular 1, tôi có thể biên dịch động một đoạn HTML và thêm nó vào DOM:

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

Câu trả lời #1

Angular 2.3.0 (07-12-2016)

Để có tất cả chi tiết, hãy kiểm tra:

  • Làm thế nào để tôi có thể sử dụng/tạo mẫu động để biên dịch Component động với Angular 2.0?

Để thấy điều đó trong thực tế:

  • quan sát một plunker đang hoạt động (hoạt động với 2.3.0+)

Các hiệu trưởng:

1) Tạo Mẫu
2) Tạo Thành Phần
3) Tạo Mô-đun
4) Biên dịch Mô-đun
5) Tạo (và lưu vào bộ nhớ đệm) ComponentFactory
6) Sử dụng Mục tiêu để tạo một Thể hiện của nó

Tổng quan nhanh về cách tạo một Thành phần

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

Cách để tiêm một component vào 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;
}

Một đoạn mã minh họa cách tạo ComponentFactory (và lưu vào bộ nhớ đệm nó)

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

Một đoạn mã minh họa cách sử dụng kết quả ở trên

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

Đọc mô tả đầy đủ với tất cả các chi tiết tại đây, hoặc quan sát ví dụ đang hoạt động

.

.

LỖI THỜI - Liên quan đến Angular 2.0 RC5 (chỉ RC5)

để xem các giải pháp trước cho các phiên bản RC trước, vui lòng tìm kiếm trong lịch sử của bài đăng này

Câu trả lời #2

Lưu ý: Như @BennyBottema đề cập trong một bình luận, DynamicComponentLoader hiện đã lỗi thời, do đó câu trả lời này cũng vậy.

Angular2 không có bất kỳ tương đương nào với $compile. Bạn có thể sử dụng DynamicComoponentLoader và thay đổi với các lớp ES6 để biên dịch mã của bạn một cách động (xem plunk này):

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

Nhưng nó chỉ hoạt động cho đến khi parser html nằm trong lõi angular2.

Câu trả lời #3

Phiên bản Angular tôi đã sử dụng - Angular 4.2.0

Angular 4 đã đưa ra ComponentFactoryResolver để tải các component trong thời gian chạy. Đây là một dạng triển khai tương tự của $compile trong Angular 1.0 phục vụ nhu cầu của bạn

Trong ví dụ dưới đây, tôi đang tải động thành phần ImageWidget vào DashboardTileComponent

Để tải một thành phần, bạn cần một chỉ thị mà bạn có thể áp dụng cho ng-template giúp đặt thành phần động.

Chỉ thị WidgetHost

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

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


      }
    }

chỉ thị này tiêm ViewContainerRef để truy cập vào vùng hiển thị của phần tử sẽ chứa thành phần được thêm động.

DashboardTileComponent (Thành phần giữ chỗ để hiển thị thành phần động)

Thành phần này chấp nhận một đầu vào đến từ các thành phần cha hoặc bạn có thể tải từ dịch vụ của mình dựa trên cách triển khai của bạn. Thành phần này đóng vai trò chính để giải quyết các thành phần tại thời gian chạy. Trong phương thức này, bạn cũng có thể thấy một phương thức có tên renderComponent() mà cuối cùng tải tên thành phần từ một dịch vụ và giải quyết với ComponentFactoryResolver và cuối cùng thiết lập dữ liệu cho thành phần động.

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

Đây là một nhà máy dịch vụ để đăng ký tất cả các thành phần mà bạn muốn giải quyết một cách động

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

ImageTextWidgetComponent (thành phần chúng tôi đang tải tại thời gian chạy)

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

Cuối cùng thêm ImageTextWidgetComponent này vào module ứng dụng của bạn như một entryComponent

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

    }
}

MôHìnhGạch

 export interface TileModel {
      data: any;
    }

Tham khảo gốc từ blog của tôi

Tài liệu chính thức

Tải xuống Mã Nguồn Mẫu

Bài viết liên quan