14

Per quanto ho notato, Angular compila le cose in un primo arrivato, base first-service che è piuttosto complicata. Ho creato una direttiva che racchiude alcuni elementi e desidero avere una proprietà link che cerchi elementi nel contenuto.AngularJS modifica l'ordine di quali direttive sono compilate

Per un caso d'uso concreto: Sto facendo una direttiva un'etichetta di ingresso che si affaccia all'interno del contenuto per il primo ingresso e aggiunge un generato in modo casuale id alla input e un attributo for al label

Ecco il codice :

// Find the first element with the attribute ng-label-target or the first input and links a label to it 
app.directive('ngLabel', function() { 
    return { 
    restrict: 'E', 
    replace: true, 
    transclude: true, 
    scope: { 
     label: '@', 
    }, 
    template: '<span class="ng-label">' + 
       '<label class="ng-label-text">{{label}}</label>' + 
       '<span class="ng-label-content" ng-transclude></span>' + 
       '</span>', 
    link: function (scope, element, attrs) { 
     scope.id = scope.id || 'random-id-' + Math.floor(Math.random() * 90000000); 
     angular.element(element[0].querySelector('.ng-label-text')). 
     attr({for:scope.id}); 

     var target = angular.element(element[0].querySelector('[ng-label-target]')); 
     if (!target || target.length == 0) { 
     target = angular.element(element[0].querySelectorAll('input,select,textarea')[0]); 
     } 
     target.attr({id:scope.id}); 
    }, 
    }; 
}); 

Esempio Uso:

<ng-label label="Text:"> 
    <input type="text" ng-model="page.textColor" size="5" maxlength="7" placeholder="e.g. #000" required /> 
    <input ng-label-target type="color" ng-model="page.textColor" required /> 
</ng-label> 

Questo di per sé funziona come un fascino.

Tuttavia ora voglio generare automaticamente diversi input e l'etichetta punta al primo. Il problema è che quando faccio un ng-repeat all'interno del mio ng-label allora il codice ng-repeat viene generato dopo che la mia direttiva è stata elaborata in modo che nulla venga effettivamente trovato.

Quindi la mia domanda è: c'è un modo in cui è possibile specificare angolare per valutare le direttive nidificate dentro e fuori anziché l'altro?

Oppure, c'è un modo migliore per farlo rispetto a quello che sto facendo attualmente?

Ho fatto un violino per esemplificare l'ordine in cui le cose vengono valutate. Si vede che l'esterno direttiva ha un valore minore o uguale di quello che è il contenuto (non può andare inferiore microsecondi così ho dovuto fare un po 'di ripetizioni):

http://jsfiddle.net/YLM9P/

+0

sembra molto più complicato del necessario ... la demo fornita ha troppa ricorsione, fornisce demo usando la direttiva e il markup. Potresti trovare più semplice il plunker per creare demo angolari rispetto al violino. – charlietfl

+0

Perché l'ID deve essere casuale? Non puoi semplicemente usare 'id =" nome _ {{$ indice}} "' e lo stesso per l'etichetta? –

+0

Non voglio specificare l'ID perché di solito non ne ho bisogno, ecco perché è casuale se non è specificato – Stefan

risposta

18

Dalla documentazione angolari:

PRIORITY Quando vi sono più direttive definite su un singolo elemento DOM, a volte è necessario specificare l'ordine in cui vengono applicati direttive. La priorità viene utilizzata per ordinare le direttive prima che vengano richiamate le loro funzioni di compilazione. La priorità è definita come un numero. Le direttive con priorità numerica maggiore vengono prima compilate. Anche le funzioni di pre-collegamento vengono eseguite in ordine di priorità, ma le funzioni di post-link vengono eseguite in ordine inverso. L'ordine delle direttive con la stessa priorità non è definito. La priorità predefinita è 0.

TERMINALE Se impostato su vero allora la priorità attuale sarà l'ultimo insieme di direttive che eseguiranno (eventuali direttive di priorità attuale sarà ancora eseguito come l'ordine di esecuzione sulla stessa priorità non è definito).

Quindi, per il tuo problema, credo che l'impostazione della proprietà del terminale su true risolverà il problema.

app.directive('ngLabel', function() { 
    return { 
    restrict: 'E', 
    replace: true, 
    transclude: true, 
    .... 
    .... 
    terminal: true 
}); 
+1

Sembra che le direttive con priorità più bassa vengano eseguite per prime. – Ghigo

0

penso che si può semplificare notevolmente il tuo codice ed elimina la tua querySelectorAll (non terribilmente angular-esque) aggiungendo un'espressione di funzione sull'elemento genitore (possibilmente passando un parametro), quindi chiamando quella funzione dagli elementi figli all'interno di ng-repeat. La funzione dovrebbe semplicemente impostare un valore che si desidera utilizzare e una volta impostato, si sa che si ha il primo e il resto deve essere ignorato. Potrei essere un po 'più specifico se mostri come dovrebbe apparire l'HTML finale con i tuoi campi di input ng-repeat.

0

Si può provare a registrare un MutationObserver che ascolta gli elementi aggiunti al DOM. Ma questo potrebbe essere un po 'in testa al vostro problema - si decide;)

var observer = new MutationObserver(function(mutations) { 
    mutations.forEach(function(mutation) { 
    // you get notified about added DOM elements here 
    // check mutation.type if it was an insertion 
    // then you handle the first node specifically. 
    console.log(mutation.target.nodeName, mutation.type, mutation); 
    }); 
}); 

var config = {childList: true, attributes: false, characterData: false, subtree: false, attributeOldValue: false, characterDataOldValue: false}; 
observer.observe(element[0], config); 
 
Demo    http://plnkr.co/h6kTtq 
Documentation  https://developer.mozilla.org/en/docs/Web/API/MutationObserver 
Browser support http://caniuse.com/mutationobserver 
1

Questo problema, come molti altri in angolare, può essere risolto con la creazione di una direttiva ulteriore:

app.directive('myLabelTarget', function() { 
return { 
    require: '^myLabel', 
    link: function(scope, element, attrs, myLabelCtrl) { 
    myLabelCtrl.doIfFirst(function(id) { 
     attrs.$set('id', id); 
    }); 
    } 
}; 
}); 

Con l'attributo require, è possibile accedere al controller della direttiva myLabel più in alto nel DOM.

Vedere questo plnkr per un esempio funzionante.

Problemi correlati