2015-06-18 38 views
46

Dato un modello per una sezione di pagina che contiene più campi e sarebbe stato popolato con dati quali come questo:componente dinamico in 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" 
     } 
    ] 
} 

mi piacerebbe avere una componente generica sezione, che non conosce i diversi tipi di campi che potrebbe avvolgere, per evitare molte logiche condizionali nel modello di sezione e per rendere nuove viste/componenti del tipo di campo aggiunti passando in un file autonomo anziché dover modificare un componente separato.

Il mio ideale sarebbe che il selettore di un componente sia abbastanza specifico da consentirmi di farlo selezionando un elemento nel modello del componente principale in base ai valori di attributo associati al modello. Per esempio: (scusate eventuali problemi di sintassi, come ho codificato questo nella finestra di SO, parte principale di prestare attenzione a è il selettore su 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() {} 
} 

... e con il tempo che vorrei aggiungere nuovo co mponents per fornire modelli e comportamenti speciali per altri specifici tipi di campo, o anche campi con determinate didascalie, ecc.

Questo non funziona perché i selettori di componente devono essere un semplice nome di elemento (in alpha.26 e alpha.27 a meno). La mia ricerca sulle conversazioni github mi porta a credere che questa restrizione sia stata rilassata, ma non posso determinare se ciò che voglio fare sarà effettivamente supportato.

In alternativa, ho visto un DynamicComponentLoader menzionato, anche se ora non riesco a trovare l'esempio che ho pensato fosse sulla guida angular.io. Anche così, non so come potrebbe essere usato per caricare dinamicamente un componente per il quale non conosce il nome o i criteri di corrispondenza.

C'è un modo per raggiungere il mio obiettivo di separare i componenti specializzati dai loro genitori usando una tecnica simile a quella che ho provato, o qualche altra tecnica di cui non sono a conoscenza in Angular 2?

UPDATE 2015-07-06

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

ho pensato che la cosa migliore per mostrare gli errori mi imbatto in modo più esplicito. Ho incluso un plunk con qualche codice di esempio, anche se solo il primo di questi 3 errori sarà visibile, poiché ognuno blocca l'altro in modo che tu possa mostrarlo solo uno alla volta. Ho programmato con difficoltà per aggirare il n. 2 e il n. 3 per il momento.

  1. Sia il mio selettore BooleanComponent è selector: 'field[type=bool]' o selector: '[type=bool]', ottengo uno stack di errore da Angular come

    Component 'BooleanComponent' può avere solo un selettore di elemento, ma ha dovuto '[type = bool]'

  2. <field [type]="field.type"></field> non associa il mio valore field.type all'attributo type, ma mi dà questo errore (che per fortuna si presenta ora in alpha 28.In alpha 26 ero in precedenza, ha fallito silenziosamente). Posso eliminare questo errore aggiungendo una proprietà type al mio componente FieldComponent e derivato BooleanComponent e collegandolo con la raccolta di proprietà @Component, ma non ne ho bisogno per nulla nei componenti.

    Impossibile associare al 'tipo' in quanto non è una proprietà sapere dell'elemento 'campo' e non ci sono direttive di corrispondenza con una corrispondente proprietà

  3. sono costretto a elencare FieldComponent e BooleanComponent nell'elenco delle direttive della mia annotazione SectionComponent View, o non verranno trovati e applicati. Ho letto le discussioni sul design del team di Angular in cui hanno preso questa decisione cosciente a favore di esplicitezza al fine di ridurre le occorrenze di collisioni con le direttive in librerie esterne, ma rompe l'intera idea di componenti drop-in che sto cercando di ottenere.

A questo punto, sto faticando a capire perché Angular2 abbia persino fastidio avere selettori. Il componente principale deve già sapere quali componenti figlio avrà, dove andranno e quali dati hanno bisogno. I selettori sono del tutto superflui al momento, potrebbe anche essere una convenzione di abbinamento del nome di classe. Non vedo l'astrazione tra i componenti che avrei bisogno di disaccoppiarli.

A causa di queste limitazioni nella capacità del framework Angular2, sto creando il mio schema di registrazione dei componenti e li posiziono tramite DynamicComponentLoader, ma sarei comunque molto curioso di vedere le risposte per le persone che hanno trovato un migliore modo per realizzare questo.

+1

grande domanda. Sto incontrando lo stesso problema. Ho trovato che in alpha-34 avremo una soluzione parziale. Qualsiasi selettore sarà consentito per i componenti. Puoi controllare qui https://github.com/angular/angular/pull/3336. Ma ancora, il problema della selezione dinamica rimane attuale. Mi piacerebbe vedere la tua soluzione con DynamicComponentLoader, se possibile. –

+0

Buona domanda, stiamo cercando di evitare di avere un blocco swtich o ngIf nella pagina. – nycynik

risposta

0

Mi ci è voluto un po ', ma vedo cosa stai cercando di fare. In sostanza, crea una libreria di moduli imperativa per ng2, come angolare-formalmente. Quello che stai cercando di fare non è coperto dai documenti, ma potrebbe essere richiesto. Le opzioni correnti possono essere trovate under annotations from lines 419 - 426.

Questa soluzione consente di selezionare in eccesso alcuni falsi positivi, ma fare un tentativo.

Change <field type="{{field.type}}"></field>-<field [type]="field.type"></field>

Quindi selezionare per l'attributo:

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

Sfortunatamente, né la modifica della sintassi del modello né il selettore suggerito funzionano effettivamente. Ho aggiornato la domanda originale per fornire dettagli sul motivo per cui non lo fanno. – DannyMeister

+2

E grazie per la menzione di angular-formly, che non avevo mai incontrato prima. Ci sono alcune somiglianze con ciò che voglio realizzare, ma voglio ancora che i miei componenti funzionino come i componenti angular2 standard con la piccola eccezione che ho più controllo su quale vista è usata per rappresentare il mio modello di dati. – DannyMeister

0

Credo che si potrebbe utilizzare Query/ViewQuery e QueryList di trovare tutti gli elementi, la sottoscrizione di cambi di QueryList e elementi di filtraggio di qualsiasi attributo , smth. in questo modo:

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

E se si desidera associare al tipo, si dovrebbe fare in questo modo:

<input [attr.type]="type"/> 
1
  1. Come l'errore dice, un componente deve avere un selettore unico. Se si desidera associare il comportamento del componente per attribuire il selettore come con [type='bool'], è necessario utilizzare le direttive. Utilizzare selector='bool-field' per il numero BoolComponent.

  2. Come dice l'errore, al componente generico <field> non è associato un attributo type. È possibile risolvere il problema aggiungendo una variabile membro di input: @Input() type: string;

  3. Si desidera delegare il modello di componente a un singolo componente che riceve un attributo type.Basta creare quel componente generico e gli altri componenti che lo usano dovranno solo fornirlo, non i suoi figli.

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

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

utilizzando il modello:

<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

Questo è esattamente il tipo di soluzione che ho detto che sto cercando di evitare. Non voglio assegnare la responsabilità di definire tutti i componenti di un singolo componente con la logica condizionale. Non so al momento della compilazione quali saranno tutte le possibili componenti. I miei clienti possono inserire nuovi componenti. Ho avuto un esempio funzionante con DynamicComponentLoader, che è stato deprecato e non ho ancora aggiornato. Per coloro che necessitano di una soluzione dinamica piuttosto che di una statica come questa proposta di risposta, suggerisco di controllare se la domanda e la risposta duplicate collegate ti aiutano. – DannyMeister

+0

Se il client può rilasciare un nuovo componente, suppongo che forniranno anche un modello. In tal caso, è possibile utilizzare [innerHtml] per iniettare il modello. Se si desidera creare un nuovo componente in fase di esecuzione, è necessario esaminare il compilatore angolare (https://angular.io/docs/ts/latest/api/compiler/TemplateCompiler-class.html) – cghislai

+0

Il compilatore del modello sembra interessante. Darei un'occhiata se dovessi tornare ad Angular dall'Aurelia (il che rende tutto molto più facile, a proposito!). – DannyMeister

Problemi correlati