2015-04-24 6 views
10

So che di solito passa funzioni alle direttive tramite un ambito isolato:modo corretto di passare funzioni alla direttiva per l'esecuzione in collegamento

.directive('myComponent', function() { 
    return { 
     scope:{ 
      foo: '&' 
     }   
    }; 
}) 

E poi nel modello possiamo chiamare questa funzione come ad esempio:

<button class="btn" ng-click="foo({ myVal: value })">Submit</button> 

Dove myVal è il nome del parametro che utilizza la funzione foo nell'ambito genitore.

Ora se intendo utilizzarlo dalla funzione link anziché dal modello, dovrò chiamarlo con: scope.foo()(value), poiché scope.foo funge da wrapper della funzione originale. Questo mi sembra un po 'noioso.

Se mi passa la funzione alla direttiva myComponent utilizzando =:

.directive('myComponent', function() { 
    return { 
     scope:{ 
      foo: '=' 
     }   
    }; 
}) 

allora sarò in grado di utilizzare solo scope.foo(value) dalla mia funzione di collegamento. Quindi questo è un caso d'uso valido per usare il binding a 2 vie sulle funzioni, o sto facendo una sorta di hack che non dovrei fare?

+0

Non vedo nulla di sbagliato in questo modo, ma forse prendere in considerazione la possibilità di delegare a un servizio. – Mosho

+0

Sono titubante perché il collegamento bidirezionale sembra essere progettato per il modello di dati, ma lo sto utilizzando su una funzione. In generale, queste funzioni non dovrebbero essere comunque modificate. –

risposta

19

Ecco il motivo per cui ho downvoted la risposta.

Innanzitutto, non utilizzare mai '=' per passare i riferimenti di funzione alle direttive.

'=' crea due orologi e li utilizza per garantire che sia l'ambito della direttiva che i riferimenti dell'ambito principale siano gli stessi (associazione bidirezionale). È una pessima idea consentire a una direttiva di cambiare la definizione di una funzione nell'ambito genitore, che è ciò che accade quando si utilizza questo tipo di associazione. Inoltre, gli orologi dovrebbero essere ridotti al minimo - mentre funzionerà, i due orologi extra $ non sono necessari. Quindi non va bene - una parte del voto negativo era per suggerire che lo fosse.

Secondo: la risposta indica erroneamente cosa fa '&'. & non è un "collegamento unidirezionale". Si ottiene quel termine improprio semplicemente perché, a differenza di '=', non crea alcun $ watch e la modifica del valore della proprietà nello scope della direttiva non si propaga al genitore.

Secondo la documentazione:

& o & attr - fornisce un modo per eseguire un'espressione nel contesto la portata genitore

Quando si utilizza & in una direttiva, genera una funzione che restituisce il valore dell'espressione valutata rispetto all'ambito principale. L'espressione non deve essere una chiamata di funzione. Può essere qualsiasi espressione angolare valida. Inoltre, questa funzione generata accetta un argomento oggetto che può sovrascrivere il valore di qualsiasi variabile locale trovata nell'espressione.

Per estendere l'esempio del PO, si supponga che il genitore usa questa direttiva nel modo seguente:

<my-component foo="go()"> 

Nella direttiva (modello o funzione di collegamento), se si chiama

foo({myVal: 42}); 

Quello che sta facendo sta valutando l'espressione "go()", che capita di chiamare la funzione "go" sull'ambito genitore, passando nessun argomento.

In alternativa,

<my-component foo="go(value)"> 

si sta valutando il "go (valore)" espressione sulla portata genitore, che è fondamentalmente chiamando $ parent.go ($ parent.value)"

<my-component foo="go(myVal)"> 

si sta valutando il "go (myVal)" espressione, ma prima che l'espressione viene valutata, myVal sarà sostituito con 42, quindi l'espressione valutata sarà "go (42)".

<my-component foo="myVal + value + go()"> 

In questo caso, $ scope.foo ({myVal: 42}) restituirà il risultato di:

42 + $parent.value + $parent.go() 

In sostanza, questo modello permette la direttiva per "iniettare" variabili che il consumatore della direttiva può opzionalmente usare nell'espressione foo.

Si potrebbe fare questo:

<my-component foo="go"> 

e nella direttiva:

$scope.foo()(42) 

$ scope.foo() valuterà l'espressione "andare", che restituirà un riferimento al $ funzione genitore.go. Quindi lo chiamerà $ parent.go (42). Lo svantaggio di questo modello è che si otterrà un errore se l'espressione non valuta una funzione.

Il motivo finale per il voto negativo è stata l'affermazione che le direttive ng-event utilizzano &. Questo non è il caso. Nessuno del costruito in direttive creare ambiti isolati con:

scope:{ 
} 

L'attuazione di '& foo' è (semplificata per chiarezza), si riduce a:

$scope.foo = function(locals) { 
    return $parse(attr.foo)($scope.$parent, locals); 
} 

L'attuazione di NG-click è simile, ma (anche semplificato):

link: function(scope, elem, attr) { 
    elem.on('click', function(evt) { 
     $parse(attr.ngClick)(scope, { 
      $event: evt 
     } 
    }); 
} 

Così la chiave da ricordare è che quando si utilizza '&', non siete passa un functio n - stai passando un'espressione. La direttiva può ottenere il risultato di questa espressione in qualsiasi momento richiamando la funzione generata.

+0

Non vedo cosa giustifica il downvote e ho fatto lo stesso per lo stesso motivo. A senso unico è solo un nome, ma spiego come valuta un'espressione. Tu, come me, riconosci che '$ scope.foo() (42)' è un anti-pattern che ha poco senso. Infine, 'ngRepeat' non crea un ambito isolato (né ng-click e la maggior parte dei comandi incorporati in direttiva, ti do questo, il mio male), quindi l'utente può ancora usare ciò che è preesistente nel suo ambito. Avresti potuto modificare la mia risposta piuttosto che duplicare le spiegazioni per servire meglio la comunità. – floribon

+2

(correzione, ho rimosso il mio downvote, non sto giocando a questo gioco) – floribon

+0

@JoeEnzminger Cosa succede se la funzione passata è una sorta di callback? Cioè deve essere richiamato con i parametri specificati nella funzione 'link' della direttiva. Va bene usare '=' in tale situazione? – kefir500

3

Il bind a due vie per passare una funzione va bene fintanto che la funzione avrà sempre gli stessi parametri nello stesso ordine. Ma anche inutile per quello scopo.

L'associazione unidirezionale è più efficiente e consente di chiamare una funzione con qualsiasi parametro e in qualsiasi ordine e offrire maggiore visibilità nell'HTML. Per esempio non potevamo immaginare ngClick essere vincolante a due vie: a volte si desidera qualcosa di simile <div ng-click="doStuff(var1)"> fino a cose più complesse come

<div ng-click="doStuff('hardcoded', var1+4); var2 && doAlso(var2)"> 

See: è possibile modificare i parametri direttamente dal codice HTML.

Ora mi sento come se avessi frainteso come utilizzare i collegamenti unidirezionali. Se infatti definisce onFoo: '&' nella vostra direttiva, poi dalla funzione di collegamento che si dovrebbe fare per esempio:

// assume bar is already in your scope 
scope.bar = "yo"; 
// Now you can call foo like this 
scope.onFoo({extra: 42}); 

Quindi nel tuo HTML è possibile utilizzare

<div on-foo="doSomething(bar, extra)"> 

Nota di avere accesso non solo tutto le proprietà dell'ambito isolato della direttiva (bar), ma anche le "persone del posto" aggiuntive aggiunte al momento della chiamata (extra).

La notazione come scope.foo()(value) mi sembra un trucco, non è il modo corretto di utilizzare i collegamenti unidirezionali.

Nota: collegamenti unidirezionali sono tipicamente utilizzati con alcune funzioni di "evento", come when-drop, on-leave, ng-click, when-it-is-loaded, ecc

+0

Perché è down down? La persona che l'ha fatto può per favore condividere qualche intuizione? –

+0

Grazie a @Xavier_Ex, sarei curioso anche di questo. Se sbaglio, non vedo l'ora di sapere esattamente cosa. – floribon

Problemi correlati