2015-06-18 5 views
46

Учитывая модель раздела страницы, которая содержит несколько полей и будут заполнены данными, такие как это:Динамический компонент в 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" 
     } 
    ] 
} 

Я хотел бы иметь общий раздел компонент, который не знает о различные типы полей, которые он мог бы обернуть, чтобы избежать множества условной логики в шаблоне раздела, а также создавать новые виды/компоненты типа поля, добавляемые путем сброса в автономный файл, а не для изменения отдельного компонента.

Мой идеал был бы для селектора компонентов достаточно конкретным, чтобы я мог выполнить это, выбрав элемент в шаблоне родительского компонента на основе значений атрибутов, привязанных к модели. Например: (простите любые проблемы синтаксиса, как я закодированы это в окне 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, хотя теперь я не могу найти пример, который, как я думал, был в руководстве angle.ioio.io. Тем не менее, я не знаю, как его можно использовать для динамической загрузки компонента, для которого он не знает имя или критерии соответствия.

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

UPDATE 2015-07-06

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

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

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

    Компонент «BooleanComponent» может иметь только селектор элемента, но имел "[тип = BOOL]

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

    не может связываться с «типа», так как это не знать свойство элемента «поля» и нет согласования директив с соответствующим свойством

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

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

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

+1

Большой вопрос. Я столкнулся с той же проблемой. Я обнаружил, что в альфа-34 мы получим частичное решение. Любой элемент управления будет разрешен для компонентов. Вы можете проверить это здесь https://github.com/angular/angular/pull/3336. Но, тем не менее, проблема динамического отбора остается актуальной. Я хотел бы увидеть ваше решение с DynamicComponentLoader, если это возможно. –

+0

Хороший вопрос, мы стараемся избегать большого блока swtich или ngIf на странице. – nycynik

ответ

0

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

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

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

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

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

К сожалению, ни синтаксис шаблона, ни предложенный селектор не работают. Я обновил исходный вопрос, чтобы предоставить подробную информацию о том, почему они этого не делают. – DannyMeister

+2

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

0

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

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

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

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

  2. Как говорится в ошибке, ваш общий компонент <field> не имеет атрибута 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> 
+0

Это именно тот тип решения, который я сказал, которого я пытаюсь избежать. Я не хочу возлагать ответственность за шаблонизацию каждого компонента на один компонент с условной логикой. Во время компиляции я не знаю, какие будут все возможные компоненты. Мои клиенты могут отказаться от новых компонентов. У меня был рабочий пример с использованием DynamicComponentLoader, который устарел, и я еще не обновил его. Для тех, кому требуется динамическое решение, а не статическое, как этот предложенный ответ, я предлагаю вам проверить, помогает ли связанный дублированный вопрос и ответ. – DannyMeister

+0

Если ваш клиент может отказаться от нового компонента, я полагаю, что он также предоставит шаблон. Если это так, вы можете использовать [innerHtml] для ввода шаблона. Если вы хотите создать новый компонент во время выполнения, вы должны изучить угловой компилятор (https://angular.io/docs/ts/latest/api/compiler/TemplateCompiler.class.html) – cghislai

+0

Компилятор шаблонов выглядит интересным. Я посмотрю, если мне когда-нибудь понадобится вернуться в Angular из Aurelia (что делает все это намного проще, кстати!). – DannyMeister

Смежные вопросы