2016-04-19 4 views
14

mit Sagen wir, ich diese einfache Liste Rendering Komponente haben:Bindungsereignisse, wenn ein ngForTemplate in Angular 2

import {Input, Component } from 'angular2/core' 

@Component({ 
    selector: 'my-list', 
    template: ` 
     <div *ngFor='#item of items' (click)='onItemClicked(item)'> 
      {{item}} 
     </div> 
    ` 
}) 
class MyList { 
    @Input() items: string[]; 

    onItemClicked(item) { console.log('Item clicked:', item); } 
} 

ich es wie folgt verwendet werden:

<my-list [items]='myAppsItems'></my-list> 

So weit so gut.

Weiter Ich entscheide, ich möchte der Benutzer in der Lage sein seine eigene Vorlage für die gerenderten Elemente zu liefern, so dass ich ändern Sie die Komponente

@Component({ 
    selector: 'my-list', 
    template: ` 
     <template ngFor [ngForOf]="items" [ngForTemplate]="userItemTemplate" (click)='onItemClicked(item)'> 
     </template> 
    ` 
}) 
class MyList { 
    @Input() items: string[]; 
    @ContentChild(TemplateRef) userItemTemplate: TemplateRef; 

    onItemClicked(item) { console.log('Item clicked:', item); } 
} 

Und es wie folgt verwenden:

<my-list [items]='items'> 
    <template #item> 
     <h1>item: {{item}}</h1> 
    </template> 
</my-list> 

Diese funktioniert nur Ich binde keine Event-Handler an die Listenelemente (plunker). Wenn ich das Click-Ereignis zu binden versuchen, wie ich in der ersten Version der Komponente hat, wirft Angular die folgende Ausnahme:

"Event binding click not emitted by any directive on an embedded template" 

hier ein plunker showing that ist. Sie können die Klickbindung löschen und es funktioniert.

Wie behebe ich das? Ich möchte nur, dass der Benutzer in der Lage ist, eine Vorlage für ein untergeordnetes Element anzugeben, das ich über ngFor iterieren werde, aber ich muss in der Lage sein, Handler an diese Elemente zu binden.

+0

Hmm, das ist ein gutes ... eine Sache, erwähnenswert - Ihre plunks beide unter Verwendung von beta-8, die ~ 2 Monate veraltet ist. Aktualisierung auf die neueste (beta-15) nicht beheben den Fehler, aber ich würde trotzdem vorschlagen http tun es – drewmoore

Antwort

10

Elementvorlage ist im App-Kontext definiert, es ist nicht klar, wie sie an den Kontext der my-list-Komponente angehängt wird. Ich habe Wrapper-Anweisung erstellen, die Vorlage und seine Variablen behandelt, Direktive ist in Div eingewickelt, um Ereignisse zu erfassen.

@Directive({ 
    selector: '[ngWrapper]' 
}) 
export class NgWrapper 
{ 
    @Input() 
    private item:any; 

    private _viewContainer:ViewContainerRef; 

    constructor(_viewContainer:ViewContainerRef) 
    { 
     this._viewContainer = _viewContainer; 
    } 

    @Input() 
    public set ngWrapper(templateRef:TemplateRef) 
    { 
     var embeddedViewRef = this._viewContainer.createEmbeddedView(templateRef); 
     embeddedViewRef.setLocal('item', this.item) 
    } 
} 
@Component({ 
    selector: 'my-list', 
    directives: [NgWrapper], 
    template: ` 
     <template ngFor #item [ngForOf]="items"> 
     <div (click)="onItemClicked(item)"> 
     <template [ngWrapper]="userItemTemplate" [item]="item"></template> 
     </div> 
     </template> 
    ` 
}) 
class MyList { 
    @Input() items: string[]; 
    @ContentChild(TemplateRef) userItemTemplate: TemplateRef; 
    userItemTemplate1: TemplateRef; 

    onItemClicked(item) { 
     console.log('Item click:', item); 
    } 

    ngAfterViewInit(){ 
     this.userItemTemplate; 
    } 
} 
@Component({ 
    selector: 'my-app', 
    directives: [MyList], 
    template: ` 
    <my-list [items]='items'> 
     <template #item="item"> 
      <h1>item: {{item}}</h1> 
     </template> 
    </my-list> 
    ` 
}) 
export class App { 
    items = ['this','is','a','test'] 

     onItemClicked(item) { 
     console.log('Item click:', item); 
    } 
} 

Die Lösung wird nicht prerfect aber fast gut, überprüfen plunkr: Es kann wie folgt verwendet werden.

+0

Siehe: // Stackoverflow.com/questions/37225722/ng-content-select-bound-variable/37229495 # 37229495 für die Verwendung von 'context' anstelle der entfernten' setLocal' (basierend auf dieser großen Antwort) –

+0

Großartig aber das scheitert mit RC4, bitte sehen das aktualisierte plunkr http://plnkr.co/edit/mFdVd9aORm2ciEfSni33?p=preview –

+0

Ich würde nicht empfehlen, diesen Ansatz (oder irgendeinen anderen mit ähnlicher Komplexität) zu verwenden, bis Angular 2 Final veröffentlicht wird. Die API ist noch nicht stabil, sodass Sie in naher Zukunft weitere Probleme bekommen können. – kemsky

14

seit einer Woche nach einer Antwort auf dieses nun auf der Suche, und ich kam schließlich mit einer recht gut Lösung auf. Anstelle von ngForTemplate würde ich vorschlagen, ngTemplateOutlet zu verwenden.

Es wird beschrieben, bereits ziemlich gut hier:

<my-list> 
    <template let-item="item"> 
    Template for: <b>{{item.text}}</b> ({{item.id}}) 
    </template> 
</my-list> 

Und die Komponentenvorlage:

<ul> 
    <li *ngFor="let item of listItems" (click)="pressed(item)"> 
    <template 
     [ngTemplateOutlet]="template" 
     [ngOutletContext]="{ 
     item: item 
     }"> 
    </template> 
    </li> 
</ul> 
angular2 feeding data back to `<template>` from `[ngTemplateOutlet]`

Die benutzerdefinierte Vorlage für die Listenelemente zwischen den Komponenten Tags platziert wird

ich habe hier ein Beispiel: https://plnkr.co/edit/4cf5BlVoqzZdUQASVQaC?p=preview

+0

Danke dafür! Ich habe gerade meinen Code aktualisiert und meine benutzerdefinierte Template-Rendering-Anweisung losgeworden. Zu Ihrer Information, Sie haben zwei '[ngOutletContext]' Bindungen in Ihrem Code. Es funktioniert nur, weil der zweite ignoriert wird. – Mud

+0

@Mud Sie sind herzlich willkommen! Mir ist aufgefallen, dass es im Android Stock Browser oder Firefox (Android) nicht funktioniert hat, ohne einen Internationalisierungspolyfill hinzuzufügen. Andernfalls wird der Fehler "Intl ist nicht definiert" ausgegeben. – Bender