Dynamic template based on value rather than variable with ngTemplateOutlet

前端 未结 3 1866
花落未央
花落未央 2021-01-13 04:51

I\'m trying to mock up a dynamic set of questions. Think of a quiz, where one question is multiple choice, the second is single answer, the third is yes no..etc.

Us

相关标签:
3条回答
  • 2021-01-13 05:30

    I would change the approach, here my 2 cents:

    Create a component for each typology of options (checkbox, radio, select, etc...).

    Store them in an constant, mapping the name of the component as string with the component class, such as:

    export const optionTypes = {
        'TypeRadio': TypeRadio,
        'TypeCheckBox': TypeCheckBox,
    };
    

    In Component:

     private optionsModule: NgModuleFactory<any>; // we have our components for our options declared in OptionsModule, for example
     private optionTypes = optionTypes;
    
     constructor(private compiler: Compiler) {
    
            // Declaring Options Module
            this.optionsModule = compiler.compileModuleSync(OptionsModule);
     }
    

    In Component's template:

    <fieldset *ngFor="let question of questions">
                    <ng-container *ngComponentOutlet="optionTypes[question.type];
                                        ngModuleFactory: optionsModule;"></ng-container>
                </fieldset>
    

    Note that for this to work, your object data should have the type attributes changed:

    questions = [
          { question: "my checkbox question", type: "TypeCheckBox", values: ["checkbox1","checkbox2","checkbox3","checkbox4"] },
          { question: "my radiobutton question", type: "TypeRadio", values: ["radio1","radio2","radio3","radio4"] }
        ];
    

    Summing up:

    1. We create an OptionsModule
    2. We create a component (with its template and logic) for each option/question type
    3. We add the name of these components in the type attribute of our data Object. (or create a simple mapping method that: radio -> TypeRadio)
    4. We use NgComponentOutlet to dynamically render our components.
    5. We use NgModuleFactory to render those components from an imported module.

    Result:

    We have a dynamic component loading system for our quizz. Each component has it's logic and offers you huge possibilities for adding cool features and behaviors!

    An example of this approach (I used this to have 100% dynamic formfields: inputs, select, radio buttons, checkboxes, etc.): Angular2: Use Pipe to render templates dynamically

    0 讨论(0)
  • 2021-01-13 05:32

    **You can change your approach a little bit as it is an optional suggestion **

    @Component({
      selector: 'my-component',
     template: `
       <div *ngFor="let item of items">
          <ng-container [ngTemplateOutlet]="item.type" [ngTemplateOutletContext]="{ item: item }"> 
          </ng-container>
       </div>
    
        <ng-template #question let-item="item"></ng-template>
        <ng-template #check let-item="item">checkboxes here {{item?.type}} - {{item?.values}}</ng-template>
        <ng-template #radio let-item="item">radio buttons here{{item?.type}} - {{item?.values}}</ng-template>
        <ng-template #bool let-item="item">boolean here{{item?.type}} - {{item?.values}}</ng-template>
        <ng-template #textbox let-item="item">textbox here{{item?.type}} - {{item?.values}}</ng-template>
    `
    })
    export class MyComponent {
      @Input() items: any[];
    }
    

    You can call your template's based on your "item.type", Rest of the structure looks good in your approach.

    0 讨论(0)
  • 2021-01-13 05:40

    As i said in comment you have to pass TemplateRef for ngTemplateOutlet property. It could be done like this:

    @Directive({
      selector: 'ng-template[type]'
    })
    export class QuestionTemplate {
      @Input() type: string;
      constructor(public template: TemplateRef) {}
    }
    

    app.html

    <my-component [items]="questions">
      <ng-template type="question" ...>...</ng-template>
      <ng-template type="check" ...>...</ng-template>
      ...
    

    my.component.ts

    @Component({
      selector: 'my-component',
      template: `
        <div *ngFor="let item of items">
            <ng-template 
               [ngTemplateOutlet]="dict['question']" 
               [ngOutletContext]="{ item: item }"></ng-template>
            <ng-template 
               [ngTemplateOutlet]="dict[item.type]" 
               [ngOutletContext]="{ item: item }"></ng-template>
        </div>`
    })
    export class MyComponent {
      @Input() items: any[];
    
      @ContentChildren(QuestionTemplate) templates: QueryList<QuestionTemplate>;
    
      dict = {};
    
      ngAfterContentInit() {
        this.templates.forEach(x => this.dict[x.type] = x.template);
      }
    }
    

    Plunker Example

    0 讨论(0)
提交回复
热议问题