Подтвердить что ты не робот

Angular 2 эквивалента ng-bind-html, $sce.trustAsHTML() и компиляции $?

В Angular 1.x мы можем вставлять HTML в режиме реального времени с помощью HTML-тега ng-bind-html в сочетании с вызовом JavaScript $sce.trustAsHTML(). Это нашло 80% пути, но не работает, когда используются теги Angular, например, если вы вставили HTML, который использовал ng-repeat или настраиваемые директивы.

Чтобы это сработало, мы могли бы использовать настраиваемую директиву, которая называется $compile.

Что эквивалентно для всего этого в Angular 2? Мы можем привязываться с помощью [inner-html], но это работает только для очень простых тегов HTML, таких как <b>. Он не преобразует настраиваемые Angular 2 директивы в функционирующие элементы HTML. (Скорее как Angular 1.x без шага $compile.) Что эквивалентно $compile для Angular 2?

4b9b3361

Ответ 1

В Angular2 вы должны использовать DynamicComponentLoader для вставки некоторого "скомпилированного контента" на странице. Так, например, если вы хотите скомпилировать следующий html:

<div>
    <p>Common HTML tag</p>
    <angular2-component>Some angular2 component</angular2-component>
</div>

тогда вам нужно создать компонент с этим html в качестве шаблона (позвоните ему CompiledComponent) и используйте DynamicComponentLoader, чтобы вставить этот компонент на страницу.

@Component({
  selector: 'compiled-component'
})
@View({
  directives: [Angular2Component],
  template: `
    <div>
      <p>Common HTML tag</p>
      <angular2-component>Angular 2 component</angular2-component>
    </div>
  `
})
class CompiledComponent {
}

@Component({
  selector: 'app'
})
@View({
  template: `
    <h2>Before container</h2>
    <div #container></div>
    <h2>After conainer</h2>
  `
})
class App {
  constructor(loader: DynamicComponentLoader, elementRef: ElementRef) {
    loader.loadIntoLocation(CompiledComponent, elementRef, 'container');
  }
}

Отметьте этот плункер

UPD Вы можете создать компонент динамически прямо перед вызовом loader.loadIntoLocation():

// ... annotations
class App {
  constructor(loader: DynamicComponentLoader, elementRef: ElementRef) {
    // template generation
    const generatedTemplate = `<b>${Math.random()}</b>`;

    @Component({ selector: 'compiled-component' })
    @View({ template: generatedTemplate })
    class CompiledComponent {};

    loader.loadIntoLocation(CompiledComponent, elementRef, 'container');
  }
}

Мне лично это не нравится, это похоже на грязный хак для меня. Но вот плункер

PS Остерегайтесь того, что в настоящий момент Angular2 находится в активной разработке. Таким образом, ситуация может быть изменена в любое время.

Ответ 2

DynamicComponentLoader устарел, вместо этого вы можете использовать ComponentResolver

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

Директива (я нашел этот код и внес некоторые изменения, вы также можете сделать это, чтобы он соответствовал вашему вкусу или использовал его как есть):

import { Component, Directive, ComponentFactory, ComponentMetadata, ComponentResolver, Input, ReflectiveInjector, ViewContainerRef } from '@angular/core';
declare var $:any;

export function createComponentFactory(resolver: ComponentResolver, metadata: ComponentMetadata): Promise<ComponentFactory<any>> {
    const cmpClass = class DynamicComponent {};
    const decoratedCmp = Component(metadata)(cmpClass);
    return resolver.resolveComponent(decoratedCmp);
}

@Directive({
    selector: 'dynamic-html-outlet',
})
export class DynamicHTMLOutlet {
  @Input() htmlPath: string;
  @Input() cssPath: string;

  constructor(private vcRef: ViewContainerRef, private resolver: ComponentResolver) {
  }

  ngOnChanges() {
    if (!this.htmlPath) return;
    $('dynamic-html') && $('dynamic-html').remove();
    const metadata = new ComponentMetadata({
        selector: 'dynamic-html',
        templateUrl: this.htmlPath +'.html',
        styleUrls:  [this.cssPath]
    });
    createComponentFactory(this.resolver, metadata)
      .then(factory => {
        const injector = ReflectiveInjector.fromResolvedProviders([], this.vcRef.parentInjector);
        this.vcRef.createComponent(factory, 0, injector, []);
      });
  }
}

Пример использования:

import { Component, OnInit } from '@angular/core';
import { DynamicHTMLOutlet } from './../../directives/dynamic-html-outlet/dynamicHtmlOutlet.directive';

@Component({
  selector: 'lib-home',
  templateUrl: './app/content/home/home.component.html',
  directives: [DynamicHTMLOutlet]
})
export class HomeComponent implements OnInit{
    html: string;
    css: string;

    constructor() {}

    ngOnInit(){
    this.html = './app/content/home/home.someTemplate.html';
    this.css = './app/content/home/home.component.css';
    }

  }

home.component.html:

<dynamic-html-outlet [htmlPath]="html" [cssPath]="css"></dynamic-html-outlet>

Ответ 3

Прочитав много, и, закрывшись, открыв новую тему, я решил ответить здесь, чтобы попытаться помочь другим. Как я уже видел, есть несколько изменений с последней версией Angular 2. (В настоящее время Beta9)

Я попытаюсь поделиться своим кодом, чтобы избежать того же разочарования, которое у меня было...

Во-первых, в нашем index.html

Как обычно, мы должны иметь что-то вроде этого:

<html>
 ****
  <body>
    <my-app>Loading...</my-app>
  </body>
</html>

AppComponent (используя innerHTML)

С помощью этого свойства вы сможете визуализировать базовый HTML, но вы не сможете сделать что-то похожее на Angular 1.x как компилируемое с помощью области:

import {Component} from 'angular2/core';

@Component({
    selector: 'my-app',
    template: `
                <h1>Hello my Interpolated: {{title}}!</h1>
                <h1 [textContent]="'Hello my Property bound: '+title+'!'"></h1>
                <div [innerHTML]="htmlExample"></div>
             `,
})

export class AppComponent {
    public title = 'Angular 2 app';
    public htmlExample = '  <div>' +
                                '<span [textContent]="\'Hello my Property bound: \'+title"></span>' +
                                '<span>Hello my Interpolated: {{title}}</span>' +
                            '</div>'
}

Это приведет к следующему:

Привет, мой Interpolated: Angular 2 приложение!

Привет, моя привязка к свойствам: Angular 2 приложения!

Привет, мой Interpolated: {{title}}

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

Существует небольшая ошибка с документами, описанными в здесь. Поэтому, если учесть, что мой код должен выглядеть следующим образом:

import {DynamicComponentLoader, Injector, Component, ElementRef, OnInit} from "angular2/core";

@Component({
    selector: 'child-component',
    template: `
        <div>
            <h2 [textContent]="'Hello my Property bound: '+title"></h2>
            <h2>Hello my Interpolated: {{title}}</h2>
        </div>
    `
})
class ChildComponent {
     title = 'ChildComponent title';
}

@Component({
    selector: 'my-app',
    template: `
                <h1>Hello my Interpolated: {{title}}!</h1>
                <h1 [textContent]="'Hello my Property bound: '+title+'!'"></h1>
                <div #child></div>
                <h1>End of parent: {{endTitle}}</h1>
             `,
})

export class AppComponent implements OnInit{
    public title = 'Angular 2 app';
    public endTitle= 'Bye bye!';

    constructor(private dynamicComponentLoader:DynamicComponentLoader, private elementRef: ElementRef) {
//        dynamicComponentLoader.loadIntoLocation(ChildComponent, elementRef, 'child');
    }

    ngOnInit():any {
        this.dynamicComponentLoader.loadIntoLocation(ChildComponent, this.elementRef, 'child');
    }
}

Это приведет к следующему:

Привет, мой Interpolated: Angular 2 приложение!

Привет, моя привязка к свойствам: Angular 2 приложения!

Привет, моя привязка свойств: заголовок ChildComponent

Привет, мой Interpolated: название ChildComponent

Конец родительского элемента: До свидания!

Ответ 4

Angular предоставляется класс DynamicComponentLoader для динамического загрузки html. DynamicComponentLoader имеют методы для вставки компонентов. loadIntoLocation является одним из них для вставки компонента.

paper.component.ts

import {Component,DynamicComponentLoader,ElementRef,Inject,OnInit} from 'angular2/core';
import { BulletinComponent } from './bulletin.component';

@Component({
    selector: 'paper',
    templateUrl: 'app/views/paper.html'

    }
})
export class PaperComponent {
    constructor(private dynamicComponentLoader:DynamicComponentLoader, private elementRef: ElementRef) {

    }

    ngOnInit(){
        this.dynamicComponentLoader.loadIntoLocation(BulletinComponent, this.elementRef,'child');

    }
}

bulletin.component.ts

import {Component} from 'angular2/core';

@Component({
    selector: 'bulletin',
    templateUrl: 'app/views/bulletin.html'
    }
})
export class BulletinComponent {}

paper.html

<div>
    <div #child></div>
</div>

Немного о чем вам нужно позаботиться:

  • Не вызывайте loadIntoLocation внутри конструктора класса. Компонентный вид еще не создан при вызове конструктора компонента. Вы получите сообщение об ошибке -

Ошибка при создании экземпляра AppComponent!. Нет компонента директива в элементе [object Object]

  • Поместите anchorName #child в html, иначе вы получите сообщение об ошибке.

Не удалось найти переменную child

Ответ 5

Посмотрите на этот модуль https://www.npmjs.com/package/ngx-dynamic-template

После долгих исследований мне помогла только эта вещь. Остальные решения, похоже, устарели.