Angular2

时间:2015-06-18 23:08:02

标签: angular

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

{
    "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"
        }
    ]
}

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

我的理想是,Component的选择器足够具体,我可以通过根据绑定到模型的属性值在父Component的模板中选择一个元素来实现这一点。例如:(原谅我在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

我认为最好显示我遇到的错误。我已经包含了一些带有一些示例代码的插件,但是这三个错误中只有第一个是可见的,因为每个错误都会阻塞另一个,所以你一次只能展示一个。我已经硬编码暂时绕过#2和#3。

  1. 我的BooleanComponent上的选择器是selector: 'field[type=bool]'还是selector: '[type=bool]',我从Angular得到一个错误堆栈
      

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

  2. <field [type]="field.type"></field>没有将我的field.type值绑定到type属性,但是给了我这个错误(幸好现在显示在alpha 28中。在alpha 26中,我以前曾在默认情况下失败)。我可以摆脱这个错误,我将一个类型属性添加到我的FieldComponent和衍生的BooleanComponent,并将其与@Component属性集合连接起来,但我不需要它用于组件中的任何内容。
      

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

  3. 我被迫在我的SectionComponent View注释的指令列表中列出FieldComponent和BooleanComponent,否则将无法找到并应用它们。我阅读了Angular团队的设计讨论,他们在这里做出有意识的决定,支持显式性,以减少外部库中指令冲突的发生,但它打破了我试图实现的插件组件的整体想法。
  4. 在这一点上,我很难理解为什么Angular2甚至不喜欢选择器。父组件必须知道它将具有哪些子组件,它们将去哪里以及它们需要什么数据。选择器目前是完全多余的,它也可以是类名匹配的约定。我没有看到我需要将它们分离的组件之间的抽象。

    由于Angular2框架能力的这些限制,我正在制作我自己的组件注册方案并通过DynamicComponentLoader放置它们,但是我仍然非常好奇看到找到更好方法的人的任何响应要做到这一点。

3 个答案:

答案 0 :(得分:1)

  1. 如错误所示,组件必须具有唯一的选择器。如果要将组件行为绑定到属性选择器,就像使用[type='bool']一样,则必须使用指令。将selector='bool-field'用于BoolComponent

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

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

  4. 示例: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>
    

答案 1 :(得分:0)

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

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

<field type="{{field.type}}"></field>更改为<field [type]="field.type"></field>

然后选择属性:

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

答案 2 :(得分:0)

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

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

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

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