web-dev-qa-db-fra.com

L'équivalent angulaire 2 de ng-bind-html, $ sce.trustAsHTML () et $ compile?

Dans Angular 1.x, nous pourrions insérer du HTML en temps réel en utilisant la balise HTML ng-bind-html, combinée à l'appel JavaScript $sce.trustAsHTML(). Cela nous a permis d'atteindre 80% du chemin mais ne fonctionnerait pas si des balises angulaires étaient utilisées, par exemple si vous insériez du HTML utilisant ng-repeat ou des directives personnalisées.

Pour que cela fonctionne, nous pourrions utiliser une directive custom appelée $ compile .

Quel est l'équivalent pour tout cela dans Angular 2? Nous pouvons lier en utilisant [inner-html] mais cela ne fonctionne que pour les balises HTML très simples telles que <b>. Il ne transforme pas les directives angulaires personnalisées 2 en éléments HTML fonctionnels. (Tout comme Angular 1.x sans l'étape $compile.) Quel est l'équivalent de $compile pour Angular 2?

16
Vern Jensen

Dans Angular2, vous devez utiliser DynamicComponentLoader pour insérer du "contenu compilé" sur la page. Ainsi, par exemple, si vous voulez compiler le code HTML suivant: 

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

vous devez ensuite créer un composant avec ce code HTML en tant que modèle (appelons-le CompiledComponent) et utiliser DynamicComponentLoader pour l'insérer sur la page.

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

Découvrez ce plunker

UPD Vous pouvez créer un composant dynamiquement juste avant l'appel 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');
  }
}

Personnellement, je n'aime pas ça, ça ressemble à un sale bidouillage pour moi. Mais voici le plunker

PS Attention, angular2 est actuellement en développement actif. Donc, la situation peut être changée à tout moment.

11
alexpods

DynamicComponentLoader est obsolète, vous pouvez utiliser ComponentResolver à la place

Vous pouvez utiliser cette directive, ajouter des canaux si vous avez besoin d'une manipulation de données supplémentaire. Cela permet également un chargement paresseux, vous n'en avez pas besoin dans votre cas, mais cela vaut la peine de le mentionner.

Directive (j'ai trouvé ce code et apporté quelques modifications, vous pouvez le faire aussi pour l'adapter à votre goût ou l'utiliser tel quel): 

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

Exemple d'utilisation:

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>
5
Daniel abzakh

Après avoir beaucoup lu et être sur le point d'ouvrir un nouveau sujet, j'ai décidé de répondre à cette question uniquement pour essayer d'aider les autres. Comme je l'ai vu, plusieurs modifications ont été apportées à la dernière version de Angular 2. (Actuellement en version bêta9)

Je vais essayer de partager mon code afin d'éviter la même frustration que moi ...

Tout d'abord, dans notre index.html 

Comme d'habitude, nous devrions avoir quelque chose comme ça:

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

AppComponent (utilisant innerHTML)

Avec cette propriété, vous serez en mesure de restituer le code HTML de base, mais vous ne pourrez pas faire quelque chose de similaire à Angular 1.x en tant que $ compile à travers une portée:

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

Cela rendra ce qui suit:

Bonjour mon application Interpolated: Angular 2!

Bonjour ma propriété lié: app angulaire 2!

Bonjour mon interpolé: {{titre}}

AppComponent Utilisation de DynamicComponentLoader

Il y a un petit bug avec la documentation, documenté dans ici . Donc, si nous avons cela à l'esprit, mon code devrait maintenant ressembler à ceci: 

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

Cela rendra ce qui suit:

Bonjour mon application Interpolated: Angular 2!

Bonjour ma propriété lié: app angulaire 2!

Bonjour ma propriété liée: titre ChildComponent

Bonjour mon titre Interpolated: ChildComponent

Fin du parent: Au revoir!

3
Jesús Sobrino

Je pense que tout ce que vous avez à faire est de définir l'élément que vous voulez avoir compilé html avec le [innerHTML] = "yourcomponentscopevar" 

2
Gregory Edwards

Consultez ce module https://www.npmjs.com/package/ngx-dynamic-template

Après une longue recherche, seule cette chose m'a aidé. Le reste des solutions semble être obsolète.

0
nostop

Classe angulaire DynamicComponentLoader fournie pour le chargement dynamique de HTML. DynamicComponentLoader ont des méthodes pour insérer des composants. loadIntoLocation est l'un d'eux pour l'insertion de composant.

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>

Peu de choses dont vous devez vous occuper:

  • N'appelez pas loadIntoLocation dans le constructeur de la classe. La vue de composant n'est pas encore créée lorsque le constructeur de composant est appelé. Vous obtiendrez une erreur - 

Erreur lors de l'instanciation de AppComponent !. Il n'y a pas de composant directive at element [objet Objet]

  • Mettez anchorName #child au format HTML, sinon vous obtiendrez une erreur.

Impossible de trouver l'enfant variable

0
Anshul