Подтвердить что ты не робот

Выбор динамического компонента в Angular2

Учитывая модель для раздела страницы, которая содержит несколько полей и будет заполнена такими данными, как:

{
    "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 на основе значений атрибутов, привязанных к модели. Например: (извините за какие-либо проблемы с синтаксисом, поскольку я закодировал это в 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() {}
}

... и со временем я добавлю новые компоненты для предоставления специальных шаблонов и поведения для других конкретных типов полей или даже полей с определенными заголовками и т.д.

Это не работает, потому что селекторы компонентов должны быть простым именем элемента (по крайней мере, в альфах .26 и альфах .27). Мое исследование разговоров github заставило меня поверить, что это ограничение было расслабленным, но я не могу определить, действительно ли будет поддерживаться то, что я хочу сделать.

В качестве альтернативы, я видел упомянутый DynamicComponentLoader, хотя теперь я не могу найти пример, который, как я думал, был в руководстве angular.io. Тем не менее, я не знаю, как его можно было бы использовать для динамической загрузки компонента, который не знает имени или критериев соответствия.

Есть ли способ выполнить мою задачу развязывания специализированных компонентов от их родителей, используя либо технику, аналогичную той, которую я пробовал, либо какую-либо другую технику, о которой я не знаю в Angular 2?

ОБНОВЛЕНИЕ 2015-07-06

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

Я подумал, что лучше показать ошибки, с которыми я сталкиваюсь более явно. Я включил плунж с некоторым примером кода, но только первая из этих трех ошибок будет видна, так как каждая из них блокирует другую, поэтому вы можете показывать только по одному. Я жестко закодирован, чтобы обойти № 2 и № 3 на данный момент.

  • Будет ли мой селектор в BooleanComponent selector: 'field[type=bool]' или selector: '[type=bool]', я получаю стек ошибок из Angular как

    Компонент "BooleanComponent" может иметь только селектор элементов, но имеет "[type = bool]"

  • <field [type]="field.type"></field> не привязывает значение моего значения field.type к атрибуту type, но дает мне эту ошибку (что, к счастью, теперь появляется в alpha 28. В alpha 26 я был ранее включен, он не прошел молча). Я могу избавиться от этой ошибки, добавив свойство type в свой FieldComponent и производный BooleanComponent и подключив его к коллекции свойств @Component, но мне это не нужно ни для чего в компонентах.

    Нельзя привязываться к "типу", так как это не свойство know элемента "field", и нет соответствующих директив с соответствующим свойством

  • Я вынужден перечислить FieldComponent и BooleanComponent в списке директив моей аннотации SectionComponent View, иначе они не будут найдены и применены. Я прочитал обсуждения в дизайне от команды Angular, где они приняли это сознательное решение в пользу объяснения, чтобы уменьшить случаи столкновений с директивами во внешних библиотеках, но это нарушает всю идею о компонентах drop-in, которые я пытаюсь достичь.

В этот момент я изо всех сил пытаюсь понять, почему Angular2 даже беспокоит наличие селекторов. Родительский компонент уже должен знать, какие дочерние компоненты он будет иметь, куда они пойдут, и какие данные им нужны. Селекторы в настоящее время совершенно лишние, это также может быть соглашение о совпадении классов. Я не вижу абстракции между компонентами, которые мне нужны, чтобы отделить их.

Из-за этих ограничений в способности структуры Angular2 я делаю свою собственную схему регистрации компонентов и размещаю их через DynamicComponentLoader, но мне все же было бы очень интересно увидеть ответы для людей, которые нашли лучший способ добиться этого.

4b9b3361

Ответ 1

  • Как говорится в ошибке, компонент должен иметь уникальный селектор. Если вы хотите связать поведение компонента с селектором атрибутов, например с [type='bool'], вам придется использовать директивы. Используйте selector='bool-field' для BoolComponent.

  • Как говорится в ошибке, ваш общий <field> компонент не имеет атрибута type для привязки. Вы можете исправить это, добавив переменную входного элемента: @Input() type: string;

  • Вы хотите делегировать шаблон компонента одному компоненту, который получает атрибут 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>

Ответ 2

Мне потребовалось некоторое время, но я вижу, что вы пытаетесь сделать. По существу создайте библиотеку императивных форм для ng2, например angular -formly. То, что вы пытаетесь сделать, не рассматривается в документах, но может быть запрошено. Текущие варианты можно найти под аннотациями из строк 419 - 426.

Это решение будет чрезмерно выбирать, приводя к некоторым ложным срабатываниям, но дайте ему попробовать.

Измените <field type="{{field.type}}"></field> на <field [type]="field.type"></field>

Затем выберите для атрибута:

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

Ответ 3

Я думаю, вы могли бы использовать Query/ViewQuery и QueryList, чтобы найти все элементы, подписаться на изменения QueryList и фильтровать элементы любым атрибутом, smth. например:

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

И если вы хотите привязать к типу, вы должны сделать это следующим образом:

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