2016-04-19 24 views
14

Powiedzmy mam ten prosty składnik lista renderingu:wiążące wydarzenia przy zastosowaniu ngForTemplate kątowej 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); } 
} 

używam go tak:

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

tej pory tak dobrze.

Następny zdecyduję chcę użytkownika, aby być w stanie dostarczyć swój własny szablon dla świadczonych przedmiotów, więc mogę zmienić komponent

@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); } 
} 

i używać go tak:

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

ten działa tylko Nie wiążę żadnych programów obsługi zdarzeń z elementami listy (plunker). Gdy próbuję wiązać zdarzenia click, jak ja w pierwszej wersji komponentu, kątowe rzuca następujący wyjątek:

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

Oto plunker showing that. Możesz usunąć wiązanie kliknięć i będzie działać.

Jak to naprawić? Po prostu chcę, aby użytkownik mógł określić szablon dla podrzędnego elementu, który zamierzam iterować przez ngFor, ale muszę mieć możliwość powiązania procedur obsługi tych elementów.

+0

Hmm, to jest dobre ... jedna rzecz warta odnotowania - twoje plunki używają beta-8, która jest ~ 2 miesiące nieaktualna. Aktualizacja do najnowszej wersji (beta-15) nie naprawia błędu, ale mimo to sugeruję zrobienie tego. – drewmoore

Odpowiedz

10

Szablon pozycji jest zdefiniowany w kontekście aplikacji, nie jest jasne, jak dołączyć go do kontekstu komponentu my-list. Mam dyrektywę wrapper, która obsługuje szablon i jego zmienne, dyrektywa jest pakowana w div, aby przechwytywać zdarzenia. Może być stosowany tak:

@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); 
    } 
} 

Rozwiązaniem nie jest prerfect ale prawie dobry, sprawdź plunkr.

+0

Zobacz http: // stackoverflow.com/questions/37225722/ng-content-select-bound-variable/37229495 # 37229495 jak używać 'context' zamiast usuniętego' setLocal' (opartego na tej wspaniałej odpowiedzi) –

+0

Wspaniale, ale to nie powiedzie się z RC4, zobacz zaktualizowany plunkr http://plnkr.co/edit/mFdVd9aORm2ciEfSni33?p=preview –

+0

nie polecałbym stosowania tego podejścia (ani żadnego innego o podobnej złożoności) do czasu wydania Angular 2 Final. Interfejs API nie jest jeszcze stabilny, więc możesz mieć więcej problemów w najbliższej przyszłości. – kemsky

14

Szukałem odpowiedzi na to od tygodnia, a ja w końcu wymyśliłem całkiem przyzwoite rozwiązanie. Zamiast używania ngForTemplate sugerowałbym użycie ngTemplateOutlet.

Jest już opisane dość dobrze tutaj: angular2 feeding data back to `<template>` from `[ngTemplateOutlet]`

Zwyczaj szablon dla elementów listy jest umieszczony pomiędzy znacznikami składowych:

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

A szablon komponent:

<ul> 
    <li *ngFor="let item of listItems" (click)="pressed(item)"> 
    <template 
     [ngTemplateOutlet]="template" 
     [ngOutletContext]="{ 
     item: item 
     }"> 
    </template> 
    </li> 
</ul> 

Podałem tutaj przykład: https://plnkr.co/edit/4cf5BlVoqzZdUQASVQaC?p=preview

+0

Dziękuję za to! Właśnie zaktualizowałem mój kod do tego i pozbyłem się mojej dyrektywy wyświetlania szablonów niestandardowych. FYI, masz dwa wiązania '[ngOutletContext]' w swoim kodzie. Działa tylko dlatego, że drugi jest ignorowany. – Mud

+0

@Mud Jesteś bardzo mile widziany! Zauważyłem, że nie działa to w przeglądarce plików Android lub Firefox (Android) bez dodawania polyfill internacjonalizacji. W przeciwnym razie wystąpi błąd "Intl nie jest zdefiniowany". – Bender

Powiązane problemy