【问题标题】:Dynamic Component selection in Angular2 [duplicate]Angular2中的动态组件选择[重复]
【发布时间】:2015-09-04 19:08:09
【问题描述】:

给定一个包含多个字段的页面部分的模型,并将填充如下数据:

{
    "fields": [
        {
            "id": 1,
            "type": "text",
            "caption": "Name",
            "value": "Bob"
        },
        {
            "id": 2,
            "type": "bool",
            "caption": "Over 24?",
            "value": 0
        },
        {
            "id": 3,
            "type": "options",
            "options" : [ "M", "F"],
            "caption": "Gender",
            "value": "M"
        }
    ]
}

我想要一个通用的部分组件,它不知道它可能包装的不同类型的字段,以避免部分模板中的大量条件逻辑,并通过删除添加新的字段类型视图/组件在一个独立的文件中,而不必修改一个单独的组件。

我的理想是组件的选择器足够具体,我可以通过基于绑定到模型的属性值在父组件的模板中选择一个元素来完成此操作。例如:(请原谅我在 SO 窗口中编码时出现的任何语法问题,需要注意的主要部分是 BooleanComponent.ts 上的选择器

SectionComponent.ts

@Component({
    selector: 'my-app'
})
@View({
    template: `
        <section>
            <div *ng-for="#field of fields">
                <field type="{{field.type}}"></field>
            </div>  
        </section>
    `,
    directives: [NgFor]
})
class SectionComponent {
    fields: Array<field>;
    constructor() {
        this.fields = // retrieve section fields via service
    }
}

FieldComponent.ts:

// Generic field component used when more specific ones don't match
@Component({
    selector: 'field'
})
@View({
    template: `<div>{{caption}}: {{value}}</div>`
})
class FieldComponent {
    constructor() {}
}

BooleanComponent.ts:

// specific field component to use for boolean fields
@Component({
    selector: 'field[type=bool]'
})
@View({
    template: `<input type="checkbox" [id]="id" [checked]="value==1"></input>`
})
class BooleanComponent {
    constructor() {}
}

...随着时间的推移,我会添加新组件,为其他特定字段类型甚至带有某些标题的字段等提供特殊模板和行为。

这不起作用,因为组件选择器必须是一个简单的元素名称(至少在 alpha.26 和 alpha.27 中)。我对 github 对话的研究让我相信这个限制正在放宽,但我无法确定我想做的事情是否真的会得到支持。

另外,我已经看到提到的 DynamicComponentLoader,但现在我找不到我认为在 angular.io 指南上的示例。即便如此,我也不知道如何使用它来动态加载它不知道名称或匹配条件的组件。

有没有办法使用类似于我尝试过的技术或我在 Angular 2 中不知道的其他技术来实现我将专用组件与其父级分离的目标?

2015-07-06 更新

http://plnkr.co/edit/fal9OA7ghQS1sRESutGd?p=preview

我认为最好更明确地显示我遇到的错误。我已经包含了一些示例代码,虽然只有这 3 个错误中的第一个是可见的,因为每个错误都会阻止另一个,所以你一次只能炫耀一个。我已经硬编码暂时绕过#2和#3。

  1. 无论我在 BooleanComponent 上的选择器是 selector: 'field[type=bool]' 还是 selector: '[type=bool]',我都会从 Angular 中得到一个错误堆栈,例如

    组件'BooleanComponent'只能有一个元素选择器,但有'[type=bool]'

  2. &lt;field [type]="field.type"&gt;&lt;/field&gt; 没有将我的 field.type 值绑定到 type 属性,但给了我这个错误(谢天谢地,现在出现在 alpha 28 中。在 alpha 26 中,我之前在,它默默地失败了)。我可以通过将类型属性添加到我的 FieldComponent 和派生的 BooleanComponent 并将其与 @Component 属性集合连接起来来消除此错误,但我不需要它用于组件中的任何内容。

    无法绑定到 'type',因为它不是 'field' 元素的已知属性,并且没有具有相应属性的匹配指令

  3. 我不得不在我的 SectionComponent View 注释的指令列表中列出 FieldComponent 和 BooleanComponent,否则它们将不会被找到和应用。我阅读了 Angular 团队的设计讨论,他们做出了这个有意识的决定来支持明确性,以减少与外部库中指令冲突的发生,但这打破了我试图实现的插入式组件的整个想法。

在这一点上,我很难理解为什么 Angular2 还要费心使用选择器。父组件已经必须知道它将拥有哪些子组件、它们将去往何处以及它们需要哪些数据。选择器目前完全是多余的,它也可以是类名匹配的约定。我没有看到我需要解耦它们的组件之间的抽象。

由于 Angular2 框架能力的这些限制,我正在制作自己的组件注册方案并通过 DynamicComponentLoader 放置它们,但我仍然很想看到找到更好方法的人的任何回应来完成这个。

【问题讨论】:

  • 好问题。我遇到了同样的问题。我发现在 alpha-34 中我们会得到部分解决方案。任何选择器都可以用于组件。你可以在这里查看github.com/angular/angular/pull/3336。但是,动态选择的问题仍然是热门话题。如果可能的话,我希望看到您使用 DynamicComponentLoader 的解决方案。
  • 好问题,我们正在努力避免在页面上出现大的 swtich 或 ngIf 块。

标签: angular


【解决方案1】:
  1. 正如错误所说,组件必须具有唯一的选择器。如果您想将组件行为绑定到属性选择器,例如[type='bool'],您必须使用指令。将selector='bool-field' 用作您的BoolComponent

  2. 正如错误所说,您的通用 &lt;field&gt; 组件没有要绑定的 type 属性。您可以通过添加输入成员变量来修复它:@Input() type: string;

  3. 您希望将组件模板委托给接收type 属性的单个组件。只需创建通用组件,其他使用它的组件只需提供它,而不是它的子组件。

示例:http://plnkr.co/edit/HUH8fm3VmscsK3KEWjf6?p=preview

@Component({
  selector: 'generic-field',
  templateUrl: 'app/generic.template.html'
})
export class GenericFieldComponent {
  @Input() 
  fieldInfo: FieldInfo;
}

使用模板:

<div>
  <span>{{fieldInfo.caption}}</span>

  <span [ngSwitch]="fieldInfo.type">
    <input  *ngSwitchWhen="'bool'" type="checkbox" [value]="fieldInfo.value === 1">  
    <input  *ngSwitchWhen="'text'" type="text" [value]="fieldInfo.value">  
    <select  *ngSwitchWhen="'options'" type="text" [value]="fieldInfo.value">
      <option *ngFor="let option of fieldInfo.options" >
        {{ option }}
      </option>
    </select>  
  </span>
</div>

【讨论】:

  • 这正是我所说的我试图避免的解决方案类型。我不想将模板每个组件的责任分配给具有条件逻辑的单个组件。我在编译时不知道所有可能的组件是什么。我的客户可以加入新组件。我有一个使用 DynamicComponentLoader 的工作示例,该示例已被弃用且尚未更新。对于那些需要动态解决方案而不是像此建议答案这样的静态解决方案的人,我建议您检查链接的重复问题和答案是否对您有帮助。
  • 如果您的客户可以加入新组件,我想他们也会提供一个模板。如果是这样,您可以使用 [innerHtml] 来注入模板。如果您想在运行时构建新组件,您应该查看 Angular 编译器 (angular.io/docs/ts/latest/api/compiler/…)
  • 模板编译器看起来很有趣。如果我需要从 Aurelia 返回 Angular,我会看看(顺便说一句,这让这一切变得容易多了!)。
【解决方案2】:

我想你可以使用 Query/ViewQuery 和 QueryList 来查找所有元素,订阅 QueryList 的更改并通过任何属性过滤元素,smth。像这样:

constructor(@Query(...) list:QueryList<...>) {
   list.changes.subscribe(.. filter etc. ..);
}

如果你想绑定到类型,你应该这样做:

  <input [attr.type]="type"/>

【讨论】:

    【解决方案3】:

    我花了一段时间,但我明白你在做什么。本质上为 ng2 创建了一个命令式表单库,比如 angular-formly。文档中未涵盖您尝试做的事情,但可以要求。当前选项可以在under annotations from lines 419 - 426 找到。

    此解决方案会过度选择导致一些误报,但请尝试一下。

    &lt;field type="{{field.type}}"&gt;&lt;/field&gt; 更改为&lt;field [type]="field.type"&gt;&lt;/field&gt;

    然后选择属性:

    @Component({
      selector: '[type=bool]'
    })
    

    【讨论】:

    • 不幸的是,模板语法改变和建议的选择器实际上都不起作用。我已经更新了原始问题,以提供有关他们为什么不这样做的详细信息。
    • 感谢您提到我以前从未遇到过的 angular-formly。与我想要完成的工作有一些相似之处,但我仍然希望我的组件像标准 angular2 组件一样工作,除了我可以更好地控制使用哪个视图来表示我的数据模型。
    猜你喜欢
    • 2016-06-30
    • 2017-02-06
    • 1970-01-01
    • 2018-03-20
    • 2016-06-30
    • 2016-07-01
    • 1970-01-01
    • 2019-04-26
    • 2023-03-17
    相关资源
    最近更新 更多