2013-08-15 14 views
21

Obiettivo: creare comportamenti utilizzando le direttive con comunicazione tra 2 elementi di pari livello (ciascuno con la propria direttiva).Comunicazione con le direttive di pari livello

Un comportamento da utilizzare nell'esempio: il contenuto dell'articolo è nascosto per impostazione predefinita. Quando si fa clic sul titolo, desidero visualizzare il contenuto dell'articolo correlato.

Il fermo: gli elementi dell'articolo correlati devono associarsi tra loro senza essere annidati in un singolo elemento padre o direttiva.

<div article="article1">this is my header</div> 
<div id="article1" article-content>this is content for the header above</div> 

<div article="article2">this is my header</div> 
<div id="article2" article-content>this is content for the header above</div> 

So che sarebbe più facile per posizionare il contenuto all'interno della direttiva articolo, tuttavia questa domanda è quello di trovare il modo di risolvere una situazione come questa.

La direttiva sul contenuto può passare in qualche modo alla direttiva relativa dell'articolo?

Questo codice non è molto utile come lo è ora, ma è un punto di partenza. Come potrei realizzare questo?

.directive('article', function(){ 
    return { 
    restrict: "A", 
    controller: function($scope) { 
     $scope.contentElement = null; 
     this.setContentElement = function(element) { 
     $scope.contentElement = element; 
     } 
    }, 
    link: function(scope, element) { 
     element.bind('click', function(){ 
     // Show article-content directives that belong 
     // to this instance (article1) of the directive 
     } 
    } 
    } 
} 
.directive('articleContent', function(){ 
    return { 
    require: "article", 
    link: function(scope, element, attrs, articleCtrl) { 
     // Maybe reference the article i belong to and assign element to it? 
     // I can't though because these are siblings. 
    } 
    } 
} 
+0

È interessante notare che, nel [$ compilare il codice sorgente di stringhe doc] (https://github.com/angular/angular.js/blob/master/src/ng/compile.js # L67) * siblingDirectiveName * è elencato come il primo possibile valore che la proprietà * require * può assumere. Sfortunatamente "siblingDirectiveName" non viene più menzionato nella documentazione e non ci sono esempi funzionanti. –

risposta

0

Se c'è un elenco di articoli e il suo contenuto si può farlo senza alcuna direttiva, utilizzando ng-repeat

<div ng-repeat="article in articles"> 
    <div article="article1" ng-click='showContent=true'>{{article.header}}</div> 
    <div id="article1" article-content ng-show='showContent'>{{article.content}}</div> 
</div> 

quindi è necessario definire il modello articolo controller. Stiamo facendo uso dell'ambito locale creato da ng-repeat.

Aggiornamento: In base al vostro feedback, è necessario collegarli together.You può provare

<div article="article1" content='article1'>this is my header</div> 
<div id="article1" article-content>this is content for the header above</div> 

e nel tuo direttiva

uso

link: function(scope, element,attrs) { 
     element.bind('click', function(){ 
     $('#'+attrs.content).show(); 
     } 
    } 

E il metodo finale potrebbe essere quello di utilizzare i metodi $rootScope.$broadcast e scope.$on per comunicare tra i controllori. Ma in questo approccio è necessario tenere traccia da dove è arrivato il messaggio e chi è il destinatario previsto che ha bisogno di elaborarlo.

+0

Non voglio ripetere su articoli. Gli articoli possono essere stati un cattivo esempio. Il mio obiettivo è creare direttive con il formato HTML specificato, consentendo la comunicazione tra i 2 elementi non nidificati appartenenti a un singolo articolo. – Coder1

31

Nessuna delle opzioni della direttiva require consente di richiedere direttive di pari livello (per quanto ne so). Si può solo:

  • richiedono sull'elemento, utilizzando require: "directiveName"
  • dicono angolare per cercare l'albero DOM usando require: "^directiveName"
  • o require: "^?directiveName" se non necessariamente bisogno il controller genitore
  • o require: "^\?directiveName" se non hai necessariamente bisogno del wrapper DOM genitore

Se vuoi comunicare con i fratelli, devi metterli in elemento DOM padre con un controller di direttiva che funge da API per la loro comunicazione. Il modo in cui viene attuato dipende in larga misura dal contesto in cui si opera.

Ecco un buon esempio da Angular JS (O Reilly)

app.directive('accordion', function() { 
    return { 
    restrict: 'EA', 
    replace: true, 
    transclude: true, 
    template: '<div class="accordion" ng-transclude></div>', 
    controller: function() { 

     var expanders = []; 

     this.gotOpened = function(selectedExpander) { 
     angular.forEach(expanders, function(expander) { 
      if(selectedExpander != expander) { 
      expander.showMe = false; 
      } 
     }); 
     }; 

     this.addExpander = function(expander) { 
     expanders.push(expander); 
     } 

    } 
    } 
}); 

app.directive('expander', function() { 
    return { 
    restrict: 'EA', 
    replace: true, 
    transclude: true, 
    require: '^?accordion', 
    scope: { title:'@' }, 
    template: '<div class="expander">\n <div class="title" ng-click="toggle()">{{ title }}</div>\n <div class="body" ng-show="showMe" \n  ng-animate="{ show: \'animated flipInX\' }"\n ng-transclude></div>\n</div>', 
    link: function(scope, element, attrs, accordionController) { 
     scope.showMe = false; 
     accordionController.addExpander(scope); 

     scope.toggle = function toggle() { 
     scope.showMe = !scope.showMe; 
     accordionController.gotOpened(scope); 
     } 
    } 
    } 
}) 

Usage (giada template):

accordion 
    expander(title="An expander") Woohoo! You can see mme 
    expander(title="Hidden") I was hidden! 
    expander(title="Stop Work") Seriously, I am going to stop working now. 
+2

Questo è un esempio perfetto e dovrebbe essere contrassegnato come risposta. – Askdesigners

9

Oppure si può creare un service solo per la comunicazione di direttiva, uno dei vantaggi di speciale service vs require è che le tue direttive non dipenderanno dalla loro posizione nella struttura html.

+0

Sembra un buon modello, puoi per favore pubblicare un esempio. – surajck

+3

'servizio C' è usato per facilitare la comunicazione tra' servizio A' e 'servizio B'. Quindi si immette il 'servizio C' sia nel' servizio A' che nel 'servizio B'. Una delle soluzioni per il codice reale è che 'service C' tenga riferimento sia a' service A' che 'service B'. Quindi dal 'servizio A' è possibile comunicare con' servizio B' come questo: 'serviceC.getServiceB(). MethodOnServiceB()' –

+2

Un'altra soluzione è quella di inviare eventi con 'servizio C' quindi' A' e 'B' o qualsiasi altro servizio che ha "iniettato il servizio C", può ascoltare quegli eventi e agire di conseguenza. –

0

Ho avuto lo stesso identico problema e sono riuscito a risolverlo.

Al fine di ottenere una direttiva per nascondere altre direttive di pari livello, ho utilizzato una direttiva padre per fungere da API. Una direttiva figlio indica al genitore che non deve essere mostrata/nascosta passando un riferimento al suo elemento, e l'altro figlio chiama la funzione di commutazione genitore.

http://plnkr.co/edit/ZCNEoh

app.directive("parentapi", function() { 
    return { 
    restrict: "E", 
    scope: {}, 
    controller: function($scope) { 
     $scope.elements = []; 

     var on = true; 
     this.toggleElements = function() { 
     if(on) { 
      on = false; 
      _.each($scope.elements, function(el) { 
      $(el).hide(); 
      }); 
     } else { 
      on = true; 
      _.each($scope.elements, function(el) { 
      $(el).show(); 
      }); 
     } 
     } 

     this.addElement = function(el) { 
     $scope.elements.push(el); 
     } 
    } 
    } 
}); 

app.directive("kidtoggle", function() { 
    return { 
    restrict: "A", 
    require: "^parentapi", 
    link: function(scope, element, attrs, ctrl) { 
     element.bind('click', function() { 
     ctrl.toggleElements(); 
     }); 
    } 
    } 
}); 

app.directive("kidhide", function() { 
    return { 
    restrict: "A", 
    require: "^parentapi", 
    link: function(scope, element, attrs, ctrl) { 
     ctrl.addElement(element); 
    } 
    } 
}); 
0

Ho avuto lo stesso problema con un Seleziona tutto/select direttiva voce che stavo scrivendo. Il mio problema era la casella di controllo Seleziona tutto era in una riga di intestazione della tabella e l'elemento selezionato era nel corpo della tabella. L'ho risolto implementando un servizio di pubblicazione/sottotitolo in modo che le direttive potessero parlare tra loro. In questo modo la mia direttiva non si preoccupava di come il mio htlm fosse strutturato. Volevo davvero usare la proprietà require, ma l'utilizzo di un servizio ha funzionato altrettanto bene.

2

Le soluzioni di cui sopra sono eccezionali e dovresti considerare l'utilizzo di un ambito genitore per consentire la comunicazione tra le tue direttive. Tuttavia, se la tua implementazione è abbastanza semplice, c'è un semplice metodo integrato in Angular che può comunicare tra due ambiti di pari livello senza utilizzare alcun genitore: $emit, $broadcast e $on.

Supponiamo, ad esempio, di disporre di una gerarchia di app piuttosto semplice con una casella di ricerca della barra di spostamento che consente di accedere a un servizio complesso e che è necessario tale servizio per trasmettere i risultati a varie altre direttive sulla pagina. Un modo per farlo sarebbe come questo:

nel servizio di ricerca

$rootScope.$emit('mySearchResultsDone', { 
    someData: 'myData' 
}); 

in alcune altre direttive/controller

$rootScope.$on('mySearchResultsDone', function(event, data) { 
    vm.results = data; 
}); 

C'è una certa bellezza al modo semplice che il codice è. Tuttavia, è importante tenere presente che la logica di trasmissione/trasmissione/trasmissione può diventare sgradevole molto rapidamente se si dispone di un sacco di luoghi diversi che trasmettono e ascoltano. Una rapida ricerca su google può far sorgere molte opinioni su quando è e non è un anti-pattern.

Qualche buona comprensione su Emit/broadcast/a in questi post:

Problemi correlati