2015-09-01 25 views
14

In Angular 1.x, è possibile inserire HTML in tempo reale utilizzando il tag HTML ng-bind-html, combinato con la chiamata JavaScript $sce.trustAsHTML(). Questo ci ha portato all'80% di quella strada, ma non funzionava quando venivano utilizzati tag Angular, ad esempio se inserivi HTML che utilizzava le direttive ng-repeat o personalizzate.

Per farlo funzionare, è possibile utilizzare uno custom directive that called $compile.

Qual è l'equivalente di tutto questo in Angular 2? Possiamo eseguire il binding utilizzando [inner-html] ma questo funziona solo per tag HTML molto semplici come <b>. Non trasforma le direttive angolari personalizzate 2 in elementi HTML funzionanti. (Molto simile ad Angular 1.x senza il passaggio $compile.) Qual è l'equivalente di $compile per Angular 2?

risposta

10

In Angular2 è necessario utilizzare DynamicComponentLoader per inserire alcuni "contenuti compilati" nella pagina. Così, per esempio, se si desidera compilare prossimo html:

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

allora avete bisogno di creare componenti con questo html come modello (chiamiamolo CompiledComponent) e utilizzare DynamicComponentLoader per inserire questa componente nella pagina.

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

Partenza this plunker

UPD È possibile creare il componente in modo dinamico a destra prima della chiamata 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'); 
    } 
} 

io personalmente non mi piace, è l'aspetto di un hack sporco me. Ma ecco the plunker

PS Attenzione che in questo momento angular2 è in fase di sviluppo attivo. Quindi la situazione può essere cambiata in qualsiasi momento.

+0

È possibile modificare l'HTML del modello in fase di esecuzione utilizzando JavaScript? L'HTML non è noto in anticipo per essere in grado di inserirlo nel modello ... è calcolato al volo. –

+0

AFAIK Non è possibile modificare le proprietà dei decoratori di componenti. E questa non è una buona idea. – alexpods

+0

Quindi, immagina lo scenario in cui hai json che ritorna dal server nel tempo. Si desidera mostrare una diff tra due di queste risposte json con una formattazione html iniettata per verde o rosso in base a se una riga è stata aggiunta o rimossa. Questo non è un caso d'uso supportato in Angular2 per quanto ne sai dato che il template è dinamico? – JimTheDev

2

Dopo aver letto molto e stando vicino all'apertura di un nuovo argomento, ho deciso di rispondere qui solo per cercare di aiutare gli altri. Come ho visto ci sono diversi cambiamenti con l'ultima versione di angolare 2. (Attualmente Beta9)

Cercherò di condividere il mio codice al fine di evitare la stessa frustrazione che ho avuto ...

in primo luogo, nel nostro index.html

come al solito, noi dovremmo avere qualcosa di simile:

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

AppComponent (utilizzando innerHTML)

Con questa struttura si sarà in grado di rendere l'HTML di base, ma non sarà in grado di fare qualcosa di simile a 1.x angolare da $ compilare attraverso un campo di applicazione:

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

Questo renderà il seguente:

Ciao my Interpolated: Angular 2 app!

Hello my Property bound: Angular 2 app!

Ciao il mio interpolata: {{title}}

AppComponent Utilizzando DynamicComponentLoader

C'è un piccolo bug con la documentazione, documentata in here. Quindi, se abbiamo in mente che, il mio codice dovrebbe essere ora in questo modo:

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

Questo renderà il seguente:

Ciao il mio interpolata: Angolare 2 app!

Hello my Property bound: Angular 2 app!

Ciao legato la mia proprietà: ChildComponent titolo

Ciao il mio interpolata: titolo ChildComponent

Fine del genitore: Bye bye!

0

Angolare fornito DynamicComponentLoader classe per il caricamento dinamico di HTML. DynamicComponentLoader hanno metodi per inserire componenti. loadIntoLocation è uno di questi per l'inserimento del componente.

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> 

Poche cose di cui hai bisogno:

  • Non chiamare loadIntoLocation all'interno del costruttore di classe. La vista componente non è ancora stata creata quando viene chiamato il costruttore del componente. Otterrai l'errore -

Errore durante l'istanziazione di AppComponent !. Non v'è alcuna componente direttiva in elemento [object Object]

  • Mettere #child anchorName in html altrimenti si otterrà l'errore.

Impossibile trovare bambino variabile

5

DynamicComponentLoader è deprecato, è possibile utilizzare ComponentResolver invece

Si potrebbe usare questa direttiva, aggiungi tubi se avete bisogno di manipolazione dei dati aggiuntivi. Permette anche il caricamento lento, non è necessario nel tuo caso, ma vale la pena menzionarlo.

direttiva (ho trovato questo codice e ha apportato alcune modifiche, si può fare anche in modo da adattarlo vostro gusto o l'uso non è):

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, []); 
     }); 
    } 
} 

Esempio come usarlo:

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> 
+0

Ciao ...Sopra la soluzione sta dando errore come sotto, Errore di dattiloscritto Modulo '"D:/app/node_modules/@ angular/core/core"' non ha membro esportato 'ComponentResolver'. Errore dattiloscritto Il modulo '"D:/app/node_modules/@ angular/core/core"' non ha un membro esportato 'ComponentMetadata'. –