【问题标题】:Dynamic template based on value rather than variable with ngTemplateOutlet使用 ngTemplateOutlet 基于值而不是变量的动态模板
【发布时间】:2017-10-19 19:33:44
【问题描述】:

我正在尝试模拟一组动态问题。想想一个测验,其中一个问题是选择题,第二个是单选题,第三个是是不是......等等。

使用 Angular 4.1,我认为使用 ngTemplateOutlet 进行模板化将是实现此目的的最佳方式,我的想法是我可以将所有复选框的样式设置为相同,并且所有单选按钮都相同等。

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

@Component({
  selector: 'my-app',
  template: `
    <div>
      <my-component [items]="questions">
        <ng-template #question let-item="item">{{item?.question}}</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>
      </my-component>
    </div>`
})
export class App {
  @ViewChild('question') question;
  @ViewChild('type') type;
  @ViewChild('values') values;
  questions = [
      { question: "my checkbox question", type: "check", values: ["checkbox1","checkbox2","checkbox3","checkbox4"] },
      { question: "my radiobutton question", type: "radio", values: ["radio1","radio2","radio3","radio4"] } ,
      { question: "my boolean question", type: "bool", values: ["yes", "no"] } ,
      { question: "my textbox question", type: "textbox", values: ["maybe something maybe nothing"] } 
    ];

我已经created this plunker as a proof of concept 努力,但它不起作用。所有代码都在src/app.ts 文件中。

我想要的是这样的:

My checkbox question?
checkbox 1, checkbox2, checkbox3

my radio button question
radiobutton1, radiobutton2, radiobutton3

my boolean question?
yes, no 

如何修改此代码以使用变量的值来指示要使用的模板?

【问题讨论】:

  • ngTemplateOutlet 应该是 TemplateRef 而你正在传递 string
  • 我会加载组件而不是模板。在我看来,为您提供更多独立的功能实现和更多的动态化。我发布了我的答案,为您提供了另一种观点,以我的拙见,这可能是一种不错的方法。 (并不是说模板加载不正确,我只是为您提供一种可能有用的不同方法)

标签: angular ng-template


【解决方案1】:

**你可以稍微改变你的方法,因为它是一个可选的建议**

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

您可以根据您的“item.type”调用您的模板,结构的其余部分在您的方法中看起来不错。

【讨论】:

    【解决方案2】:

    正如我在评论中所说,您必须将 TemplateRef 传递给 ngTemplateOutlet 属性。可以这样做:

    @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

    【讨论】:

    • 您在代码中使用 @Directive,自 2 RC 之前的版本以来已弃用。在这种情况下,这与组件相同吗?我使用 Angular 的时间还不够长,无法对指令或如何将它们转换为组件有任何经验。
    • 您在哪里看到 @Directive 已弃用?
    • 看来这可能只是我的无知。角度存在 3 种类型的指令,第一种是组件。它从指令到组件的变化让我看起来像。
    • 当我查看您的示例时,它非常适用于动态类型。但是没有使用问题模板。这是为什么呢?
    • 什么意思? type=question?您的数组中没有此类类型的元素
    【解决方案3】:

    我会改变方法,这里是我的 2 美分:

    为每种类型的选项(复选框、单选、选择等)创建一个组件。

    将它们存储在一个常量中,将组件名称作为字符串与组件类进行映射,例如:

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

    在组件中:

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

    在组件的模板中:

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

    请注意,要使其正常工作,您的对象数据应更改 type 属性:

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

    总结:

    1. 我们创建一个 OptionsModule
    2. 我们为每个选项/问题类​​型创建一个组件(及其模板和逻辑)
    3. 我们将这些组件的名称添加到数据对象的type 属性中。 (或创建一个简单的映射方法:radio -> TypeRadio
    4. 我们使用NgComponentOutlet 来动态渲染我们的组件。
    5. 我们使用NgModuleFactory 从导入的模块中呈现这些组件。

    结果:

    我们的测验有一个动态组件加载系统。每个组件都有其逻辑,并为您提供添加酷特性和行为的巨大可能性!

    这种方法的一个例子(我用它来拥有 100% 的动态表单域:输入、选择、单选按钮、复选框等)Angular2: Use Pipe to render templates dynamically

    【讨论】:

    • 你在任何地方都没有这种东西吗?
    • @paqogomez no :( 我在公共访问中最接近的是链接到我解释我的解决方案的帖子(这种方法)。对不起,但很容易使用,检查文档并遵循这个答案或我链接的那个,你应该没问题。如果你决定走这条路,请告诉我,我可以帮助你
    • 我又回到了这个问题上,但我有几个问题。我已经创建了选项模块(但我不确定其中的内容),类型组件和数据对象已更改以反映类型。但我不确定如何根据您的 cmets 将其连接在一起。
    • 另外,我可以将question 对象传递给我的类型组件吗?
    猜你喜欢
    • 2019-07-28
    • 2021-09-14
    • 1970-01-01
    • 2017-09-20
    • 2022-01-15
    • 1970-01-01
    • 2018-02-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多