2016-07-06 36 views
13

Sto sviluppando un'applicazione in ng2 e sto lottando con qualcosa. Sto creando un calendario in cui puoi scegliere un intervallo di date e devo rispondere agli eventi click & mouseenter/mouseleave nelle celle del giorno. Così ho un codice (semplificato) in questo modo:Delega eventi in Angular2

calendar.component.html

<month> 
    <day *ngFor="let day of days" (click)="handleClick()" 
     (mouseenter)="handleMouseEnter()" 
     (mouseleave)="handleMouseLeave()" 
     [innerHTML]="day"></day> 
</month> 

Ma questo mi dà centinaia di ascoltatori di eventi separati nella memoria del browser (cellule di ogni giorno ottiene 3 listener di eventi e posso avere fino a 12 mesi di visualizzazione alla volta, quindi sarebbe più di 1k di ascoltatori).

Quindi volevo farlo "nel modo giusto", usando il metodo chiamato "delegazione di eventi". Voglio dire, allegare un evento click sul componente principale (month) e quando riceve un evento click, è sufficiente verificare se si è verificato sul componente Day - e solo allora avrei reagito a questo clic. Qualcosa come jQuery fa nel suo on() method quando lo passi il parametro selector.

Ma lo facevo facendo riferimento agli elementi del DOM in modo nativo nel codice del gestore:

month.component.ts

private handleClick(event) { 
    if (event.target.tagName === 'DAY') { 
     // handle day click 
    } else { 
     // handle other cases 
    } 
} 

ei miei colleghi respinti mia idea, dal momento che - come hanno detto - "Ci deve essere un modo più semplice e corretto di gestirlo in NG2, come in jQuery. Inoltre, sta andando fuori controllo qui - stai reagendo ai clic di Day nel codice del mese."

Quindi, la mia domanda è, c'è un modo migliore? O sto cercando di risolvere un problema che non dovrei più preoccuparmi di risolvere, dal momento che i dispositivi degli utenti ottengono sempre più memoria/potenza di elaborazione ogni giorno?

Grazie in anticipo!

+1

Ci sono moduli autonomi che gestisce questo. Trovo che "dom-delegate" funzioni molto bene. Non c'è niente di built-in in ng2 per quanto ne so. – Chrillewoodz

+1

Se questo è un problema ... sarebbe un problema di ottimizzazione in angolare. Non dovresti preoccuparti di aggregare eventi per ottimizzare. –

+1

Ho avuto un problema simile alcuni mesi fa, da quello che so, il metodo che hai descritto è il modo migliore per farlo con il raw angolare. controllando i target di clic con meno ascoltatori di eventi. l'unica cosa che suggerirei è mettere la logica in un servizio o in una direttiva (la direttiva potrebbe funzionare meglio dal momento che lo si desidera ogni div ogni mese) in questo modo il codice è pulito e separato dal componente. Dopo tutto, è la logica html, non la logica dei componenti – FussinHussin

risposta

1

Intro

ci siamo imbattuti in questo oggi, e posso veramente vedere la necessità di questa implementazione in un sacco di applicazioni. Ora non posso garantire che questa sia la migliore tecnica al 100%, tuttavia mi sono impegnato a rendere questo approccio il più angoloso possibile.

L'approccio che ho trovato ha 2 fasi. Sia la fase 1 che la fase 2 aggiungeranno un totale di years * months + years * months * days, quindi per 1 anno si avranno gli eventi 12 + 365.


fase Ambito

Fase 1: eventi delegato quando un mese fa clic giù nel giorno stesso che è stato cliccato senza richiedere un evento il giorno.
Fase 2: Propagare il giorno scelto di nuovo al mese.

Poco prima scavare in, l'applicazione è costituito da 3 componenti che sono annidati nel seguente ordine: app => month => day


Ciò è tanto html che è richiesto. app.il componente ospita un numero di mesi, month.component ospita un numero di giorni e day.component non fa altro che visualizzarlo come testo.

app.component.html

<app-month *ngFor="let month of months" [data-month]="month"></app-month> 

month.component.html

<app-day *ngFor="let day of days" [data-day]="day">{{day}}</app-day> 

day.component.html

<ng-content></ng-content> 

Questo è piuttosto roba standard di serie.


Fase 1

Diamo uno sguardo al month.component.ts dove vogliamo delegare il nostro evento da.

// obtain a reference to the month(this) element 
constructor(private element: ElementRef) { } 

// when this component is clicked... 
@HostListener('click', ['$event']) 
public onMonthClick(event) { 
    // check to see whether the target element was a child or if it was in-fact this element 
    if (event.target != this.element.nativeElement) { 
    // if it was a child, then delegate our event to it. 
    // this is a little bit of javascript trickery where we are going to dispatch a custom event named 'delegateclick' on the target. 
    event.target.dispatchEvent(new CustomEvent('delegateEvent')); 
    } 
} 

Sia fase 1 e 2, v'è solo 1 avvertimento e che è; se si hanno nidificato elementi figlio all'interno della vostra day.component.html, sarà necessario implementare uno spumeggiante per questo, una migliore logica nel senso che se dichiarazione, o un trucco veloce sarebbe .. in day.component.css:host *{pointer-events: none;}


Ora abbiamo bisogno di raccontare la nostra day.component aspettarsi il nostro evento delegateEvent. Quindi, in day.component.ts tutto quello che dovete fare (nel modo più angolare possibile) è ...

@HostListener('delegateEvent', ['$event']) 
public onEvent() { 
    console.log("i've been clicked via a delegate!"); 
} 

Questo funziona perché dattiloscritto non si preoccupa se l'evento è nativo o no, sarà solo associare un nuovo evento javascript per l'elemento e quindi ci consente di chiamarlo "nativamente" tramite event.target.dispatchEvent come facciamo in precedenza in month.component.ts.

Concludendo la Fase 1, ora stiamo delegando con successo gli eventi dal nostro mese ai nostri giorni.


Fase 2

Che cosa succede se diciamo vuole correre un po 'di logica nel nostro evento delegato entro day.component e poi tornare a month.component - in modo che possa poi proseguire con la sua possedere la funzionalità in un metodo molto orientato agli oggetti? Bene per fortuna, possiamo facilmente implementarlo!

Nell'aggiornamento month.component.ts di seguito. Tutto ciò che è cambiato è che ora stiamo per passare una funzione con la nostra invocazione all'evento e abbiamo definito la nostra funzione di callback.

@HostListener('click', ['$event']) 
    public onMonthClick(event) { 
    if (event.target != this.element.nativeElement) { 
     event.target.dispatchEvent(new CustomEvent('delegateEvent', { detail: this.eventDelegateCallback})); 
    } 
    } 

    public eventDelegateCallback(data) { 
    console.log(data); 
    } 

Tutto ciò che rimane è quello di richiamare questa funzione all'interno day.component.ts ...

public onEvent(event) { 
    // run whatever logic you like, 
    //return whatever data you like to month.component 
    event.detail(this.day); 
} 

Purtroppo la nostra funzione di callback è un po 'ambiguo di nome qui, tuttavia dattiloscritto si lamenterà la proprietà non essendo un oggetto definito letterale CustomEventInit se chiamato in altro modo.


Imbuto eventi Diverse

L'altra cosa interessante di questo approccio è che non si dovrebbe mai essere necessario definire più di questo numero di eventi, perché si può solo incanalare tutti gli eventi attraverso questa delega e quindi eseguire la logica entro day.component.ts per filtrare per event.type ...

month.component.ts

@HostListener('click', ['$event']) 
@HostListener('mouseover', ['$event']) 
@HostListener('mouseout', ['$event']) 
    public onMonthEvent(event) { 
    if (event.target != this.element.nativeElement) { 
     event.target.dispatchEvent(new CustomEvent('delegateEvent', { detail: this.eventDelegateCallback })); 
    } 
    } 

day.component.ts

private eventDelegateCallback: any; 

@HostListener('delegateEvent', ['$event']) 
    public onEvent(event) { 
    this.eventDelegateCallback = event.detail; 
    if(event.type == "click"){ 
     // run click stuff 
     this.eventDelegateCallback(this.day) 
    } 
    } 
+0

O non capisco il tuo concetto o Hai frainteso quello che stavo cercando di ottenere. Volevo avere solo N ascoltatori di eventi, dove N è il numero di mesi. Il tuo approccio crea esattamente lo stesso numero di ascoltatori di eventi semplicemente come eventi di click su HostBinding su DayComponents. È solo che stai vincolando eventi personalizzati. –

+0

Gli eventi multipli, tuttavia, sono quando può effettivamente portare profitto :) –

+0

@MichalLeszczyk Non ha raggiunto il punto N ma è un miglioramento significativo e non aumenta in modo esponenziale quando si aggiunge un nuovo tipo di evento. Non è perfetto, ma penso che sia sulla strada giusta! – Zze