2013-04-18 4 views
6

sto cercando di iniettare 2 modelli in un elemento e operare su di essi:AngularJS: molteplici direttive con inclusione sullo stesso elemento

<div 
    ic-first="foo" 
    ic-second="bar" 
    ic-third="baz" 
    ic-fourth="qux" 
> 
</div> 

icFirst dovrebbero iniettare attraverso un modello di un div vuoto come un bambino di il suo elemento. icSecond dovrebbe iniettare un secondo div (con un gruppo di contenuto) come il secondo figlio del suo elemento, in modo che il codice HTML risultante sarà simile:

<div 
    ic-first="foo" // priority: 100 
    ic-second="bar" // priority: 50 
    ic-third="baz" // priority: 0 
    ic-fourth="qux" // priority: 0 
> 
    <div id="foo"></div> 
    <div> <!-- a bunch of stuff from the templateUrl --> </div> 
</div> 

Sia icFirst e icSecond inietterà altri elementi nei contenitori appena creati.

Quando ho specificare una proprietà modello direttiva sulle due direttive, ottengo un errore:

Error: Multiple directives [icFirst, icSecond] asking for template on: <div ic-first

Quando aggiungo transclude: true ad entrambe le direttive, icFirst esegue bene ... ma poi le altre direttive sulla stessa l'elemento non viene eseguito. Quando imposto lo transclude: 'element', le altre direttive eseguono ma. Ricevo un errore che il primo figlio ($scope.firstObj) non è definito.

Tutti e quattro direttive bisogno di accedere a portata di ciascuno, in modo che sto facendo la maggior parte del mio lavoro nei loro controllori:

app.directive('icFirst', ['ic.config', function (icConfig) { 
    return { 
    restrict: 'A', 
    priority: 100, 
    template: '<div id="{{firstId}}"></div>', 
    replace: false, 
    transclude: 'element', 
    controller: function icFirst($scope, $element, $attrs) { 
     // … 
     $scope.firstId = $scope.opts.fooId; 
     $scope.firstElm = $element.children()[0]; 
     $scope.firstObj = {}; // this is used by the other 3 directives 
    }, 
    link: function(scope, elm, attrs) { … } // <- event binding 
    } 
); 
app.directive('icSecond', ['ic.config', function (icConfig) { 
    return { 
    restrict: 'A', 
    priority: 0, 
    templateUrl: 'views/foo.html', 
    replace: false, 
    transclude: 'element', 
    controller: function icSecond($scope, $element, $attrs) { 
     // … 
     $scope.secondElm = $element.children()[1]; 
     $scope.secondObj = new Bar($scope.firstObj); 
     //^is used by the remaining 2 directives & requires obj from icFirst 
    }, 
    link: function(scope, elm, attrs) { … } // <- event binding 
    } 
); 

Nota Ho corretto il comportamento di replace: false per abbinare il comportamento documentato, come descritto nella richiesta pull #2433.

Ho provato a istanziare $scope.firstObj nel controller e impostarlo nel linkFn (sperando che la transizione si sarebbe conclusa nel momento in cui il linkFn viene eseguito), ma ottengo lo stesso problema. Sembra che il primo figlio sia in realtà un commento.

risposta

1

L'unico motivo che posso venire con questo spiega gettando questo errore è che la squadra AngularJS stava cercando di evitare sovrascritture inutili/DOM manipolazione:

Considerando il comportamento effettivo della replace: false contro il comportamento documentato, penso che il il reale è in realtà il comportamento previsto. Se questo è vero, consentire l'uso di più template/templateUrls sullo stesso elemento farà sì che i template successivi sovrascrivano quelli precedenti.

Dato che già modificato la sorgente adatta al comportamento documentato, come una soluzione rapida , ho modificato nuovamente (/src/ng/compile.js:700) la fonte per rimuovere il controllo assertNoDuplicate (che corrisponde a angular.js:4624). Ora torno i seguenti 2 oggetti, e funziona, e non riesco a trovare alcuna ripercussione negativa:

// directive icFirst 
return { 
    restrict: 'A', 
    priority: 100, 
    replace: false, 
    template: '<div id="{{firstId}}"></div>', 
    require: ["icFirst"], 
    controller: Controller, 
    link: postLink 
}; 
// directive icSecond 
return { 
    restrict: 'A', 
    require: ['icFirst'], 
    replace: false, 
    templateUrl: 'views/bar.html', 
    priority: 50, 
    controller: Controller, 
    link: postLink 
}; 

Se reso permanente, il controllo dovrebbe probabilmente essere
if (directive.templateUrl && directive.replace)
(e simile per direttiva.template)

+2

Ora c'è un modo più semplice.Basta aggiungere '$$ tlb: true' alla direttiva e ignorerà l'asserzione. –

+1

@JonathanRowny il '$$' indica che '$$ tlb' è 'privato' per API e non deve essere usato al di fuori dell'API, in quanto gli sviluppatori possono decidere di interromperlo in qualsiasi momento. –