web-dev-qa-db-fra.com

Charger des modèles dynamiques dans un seul composant avec l'aide de Angular 4

Mon exigence est de construire un composant qui a 2 ou plusieurs modèles HTML où chaque modèle HTML a au moins 20 contrôles et basé sur quelques conditions, charge ce modèle spécifique. 

Remarque: j'ai choisi 3 modèles différents car les contrôles varient en fonction du type de fichier templateType, dans lequel un fichier unique en tant qu'entrée et la logique permettant de générer et de sauvegarder les valeurs du modèle restent les mêmes. J'ai donc décidé de choisir 3 modèles et un fichier ts comme composant unique.

//sub.component.ts
@Component({
     selector: 'sub-component',
     template: `
               <div [ngSwitch]="templateType">
                    <ng-template *ngSwitchCase="'1'"> ${require('./sub1.component.html')} </template>
                    <ng-template *ngSwitchCase="'2'"> ${require('./sub2.component.html')} </template>
                    <ng-template *ngSwitchCase="'3'"> ${require('./sub3.component.html')} </template>
                    <ng-template ngSwitchDefault> ${require('./sub1.component.html')} </template>
</div>
    `
})

J'ai essayé l'option ci-dessus, car elle apparaît comme une solution simple pour obtenir le comportement, mais l'échec de la compilation avec l'impossibilité de trouver est impossible. Dans AngularJS, nous avons ng-Include pour renseigner tous les modèles, mais il semble que ng-template ne prend pas en charge le chargement de contenu HTML externe.

Veuillez ne pas marquer ceci comme étant en double, car de nombreuses requêtes semblables à celle-ci apparaissent, mais la plupart des solutions sont obsolètes ou ne sont pas applicables pour Angular 4. Veuillez indiquer une alternative au lieu de joindre des liens différents.

7
BVS

Je sais que cette question est ancienne, mais j'espère que cela aidera les personnes qui tentent des choses similaires. 

Ce qui est malheureux à propos de ce que vous voulez, c'est que le moyen le plus simple de le faire est simplement de faire de chaque modèle son propre composant. Sinon, vous devez injecter et assainir le code HTML. Ils ont supprimé ng-include et des fonctionnalités similaires en raison des risques de sécurité liés à l’injection de code HTML non assaini. Ce ne serait pas vraiment un P.I.T.A si vous n'aviez pas à importer et déclarer spécifiquement tous ces composants supplémentaires dans votre module, mais hélas ... 

Vous pouvez créer une directive simple qui obtiendra templateRefs, puis interroger des éléments de votre page contenant ces directives, en extraire la référence du modèle et les insérer ailleurs. Cela vous permettrait au moins de conserver tous les modèles dans un fichier séparé. J'ai l'habitude de mettre 3 ou 4 modèles dans un composant séparé et de les inclure dans le composant qui veut les rendre. Je vais décrire comment faire cela. 

Directive pour obtenir des modèles de référence 

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

@Directive({
 selector: 'get-template',
})
export class GetTemplateDirective {
  @Input() name: string;
  constructor(public template: TemplateRef<any>) {  }
}

Ensuite, pour les modèles, créez un composant super simple qui les a tous

@Component({
  selector: 'sub-component-templates',
  template: `

<ng-template get-template [name]="tpl1">
  Put Whatever here, including other components if you please
</ng-template> 

<ng-template get-template [name]="tpl2">
  Different template here
</ng-template> 

 ... and so on and so on...
`
})
export class Templates { }

Importez tous les nouveaux composants pertinents dans votre module, puis incluez-les dans votre composant principal qui rendra les modèles.

Je le fais habituellement avec ng-content, de sorte qu'il est clair dans le composant parent que ce composant en référence un autre pour ses modèles. 

Par exemple, dans le parent ..

<sub-component>
    <sub-component-templates></sub-component-templates>
</sub-component>

Puis dans le sous composant 

 import { Component, ViewChild, ContentChildren, QueryList } from '@angular/core';
import { GetTemplateDirective } from 'wherever';

@Component({
selector: 'sub-component',
template: `

<ng-content></ng-content>

 <div #templateRenderer></div>
`
})
export class SubComponent {

@ViewChild('templateRenderer',{read:ViewContainerRef}) anchor: ViewContainerRef;
@ContentChildren(GetTemplateDirective) templates: QueryList<GetTemplateDirective>;

ngAfterContentInit()  {

  ... at this stage, you will have access to all the included templates with that directive on them. You can perform your logic to choose which one you want. Once you have selected the proper one, you can embed it like so ... 
 let desiredTemplateName = 'whatever';

     for (let t of this.templates.toArray()) {
       if(t.name === desiredTemplateName) {
          this.anchor.createEmbeddedView(t.template);
          break;        
       } 
     }  
   }

}

Vous pouvez voir que c'est ridiculement compliqué pour ce que vous essayez de faire. Il serait plus facile de simplement les créer en tant que composants séparés et d’utiliser ngSwitchCase pour choisir celui qui convient. L’avantage de la méthode que j’ai décrite ci-dessus est qu’elle vous permet de conserver vos modèles où vous voulez, et que vous pouvez en inclure 100 dans le même composant externe (qui n’est en réalité que le composant minimal décoré avec un modèle) si vous vouliez, ou les déplacer avec un service, ou autre chose. 

Voir ici pour un exemple pratique d'utilisation du compilateur - https://plnkr.co/edit/fdP9Oc?p=info

Encore assez compliqué ... 

Si vous stockez le modèle en tant que propriété de la classe, vous pourrez le modifier ultérieurement, si nécessaire. Il suffit d'ajouter un modèle d'importation de référence ...

 import { Component, ViewChild, ContentChildren, QueryList, TemplateRef } from '@angular/core';

puis créer une propriété 

 template: TemplateRef<any>;

Ensuite, vous pourrez le remplacer par un de votre liste de requêtes et créer à nouveau la vue intégrée à l'aide des méthodes du conteneur de vues.

Angular 2/4 a facilité certaines choses ... et rendu certaines choses plus difficiles. Mais je suppose que dans ce cas, c’est au nom de la sécurité. 

9
diopside

En fait, j'essaie de comprendre cela au moment où nous parlons et lorsque j'ai trouvé la réponse ci-dessus, cela m'a donné une idée que je suis sur le point d'essayer. J'espère avoir du succès avec cela et pouvoir mettre à jour cette réponse avec quelque chose de plus concret.

Je travaille toujours sur mes questionnaires et j'ai des questions à choix multiples avec des réponses formulées, des questions à choix multiples avec un mécanisme de type "échelle de 1-10", puis des questions nécessitant une réponse textuelle.

Je pense que créer un component et encapsuler chaque modèle dans une condition ngIf qui se connecte à une variable de la classe dans laquelle les données seront transmises peut déclencher le gabarit.

Donc, les données peuvent ressembler à quelque chose comme ça

Questions:[{
    question:'blahblahblah',
    answers: [..array of answers..],
    template: 'a'
    },
    {
    question: 'yaddayaddayadda',
    answers: [],
    template: 'b'
    },
    {
    etc.
    }
    ]

Ensuite, dans la classe de composant, vous pouvez avoir quelque chose comme ça

@Component ({
    selector: 'question-component',
    templateUrl: './template.html'
})

export class QuestionComponent {

    @input() data: yourDataType;

    constructor() {} 

}

puis dans le modèle pour ce composant ont quelque chose comme

<div *ngIf="data.template === a">
    <!-- code for template with binding and everything -->
</div>

<div *ngIf="data.template === b">
    <!-- code -->
</div>

<!-- etc. etc. etc. -->

Ensuite, dans le modèle du composant principal, vous pouvez faire quelque chose comme

<div *ngFor = "question of Questions">
    <question-component [data]="question"></question-component>
</div>

C’est une hypothèse qui me vient à l’esprit, alors il me manque peut-être certaines choses, mais j’estime que cela vaut la peine d’avoir ici la possibilité de commencer à fouiller dans l’intensité. Je vais voir si je peux le faire fonctionner pour mes besoins.

0
Optiq