2013-04-05 13 views
7

Il problema è che devo gestire un elenco di palline di gomma recuperate da un servizio. La direttiva che ho creato sembra funzionare quando ho hardcode gli elementi nell'HTML, ma quando provo ad allocare dinamicamente le palline di gomma usando una ng-repeat.Direttiva angularJS con ng-repeat non redering

HTML

<div ng-controller="GumballsCtrl"> 

<h1>Working</h1> 
    <ul> 
     <li ng-repeat="gumball in Gumballs"> 
      <div class="gumballColor{{gumball.color}}">{{gumball.color}}</div> 
     </li> 
    </ul> 

<h1>Problem - Expecting the same result at the work version</h1> 

    <ul> 
     <li ng-repeat="gumball in Gumballs"> 
      <mygumball id={{gumball.id}} color="{{gumball.color}}">{{gumball.color}}</mygumball> 
     </li> 
    </ul> 
</div> 

JavaScript

var myApp = angular.module('myApp', []); 

function GumballsCtrl($scope, Gumballs) { 
    $scope.Gumballs = Gumballs; 
} 

myApp.factory('Gumballs', function() { 
    return [{ 
     id: '1', 
     color: 'R' 
    }, { 
     id: '2', 
     color: 'G' 
    }, { 
     id: '3', 
     color: 'B' 
    }, { 
     id: '4', 
     color: 'Y' 
    }, { 
     id: '5', 
     color: 'G' 
    }]; 
}); 

myApp.directive('mygumball', function ($scope) { 
    return { 
     restrict: 'E', 

     scope: {}, 

     link: function (scope, element, attrs) { 
      if (attrs.color !== '' && attrs.color !== undefined) { 
       scope.color = attrs.color; 
      } else { 
       scope.color = 'U'; 
      } 
     }, 

     replace: true, 

     template: "<div class='gumballColor{{color}}'>{{color}}</div>" 
    }; 
}); 

CSS

.gumballColorR { 
    font-size: 12px; 
    text-align: center; 
    padding: 2px; 
    -moz-border-radius: 10px; 
    -webkit-border-radius: 10px; 
    border-radius: 10px; 
    border: solid 1px #CC0000; 
    background-color: #FF0000; 
    width: 15px; 
    height: 15px; 
    margin-left: 5px; 
    margin-top: 5px; 
} 
.gumballColorG { 
    font-size: 12px; 
    text-align: center; 
    padding: 2px; 
    -moz-border-radius: 10px; 
    -webkit-border-radius: 10px; 
    border-radius: 10px; 
    border: solid 1px #00CC00; 
    background-color: #00FF00; 
    width: 15px; 
    height: 15px; 
    margin-left: 5px; 
    margin-top: 5px; 
} 
.gumballColorB { 
    font-size: 12px; 
    text-align: center; 
    padding: 2px; 
    color: #FFFFFF; 
    -moz-border-radius: 10px; 
    -webkit-border-radius: 10px; 
    border-radius: 10px; 
    border: solid 1px #0000CC; 
    background-color: #0000FF; 
    width: 15px; 
    height: 15px; 
    margin-left: 5px; 
    margin-top: 5px; 
} 
.gumballColorY { 
    font-size: 12px; 
    text-align: center; 
    padding: 2px; 
    -moz-border-radius: 10px; 
    -webkit-border-radius: 10px; 
    border-radius: 10px; 
    border: solid 1px #CCCC00; 
    background-color: #FFFF00; 
    width: 15px; 
    height: 15px; 
    margin-left: 5px; 
    margin-top: 5px; 
} 
.gumballColorU { 
    font-size: 12px; 
    text-align: center; 
    padding: 2px; 
    -moz-border-radius: 10px; 
    -webkit-border-radius: 10px; 
    border-radius: 10px; 
    border: solid 1px #CCCCCC; 
    background-color: #DDDDDD; 
    width: 15px; 
    height: 15px; 
    margin-left: 5px; 
    margin-top: 5px; 
} 

http://jsfiddle.net/i3sik/NGB9v/22/

L'id e il colore attributi quando passarono nella Diretti finiscono per essere indefiniti quando passano usando la ng-repeat, ma funzionano quando sono codificati nell'HTML.

Grazie mille per l'aiuto che puoi fornire.

risposta

10

Il problema qui è il tuo ambito isolato. Usando scope: {} hai creato un nuovo ambito isolato per agire su quell'elemento. Gli ambiti isolati non ereditano dall'ambito principale. Tutti gli attributi e il contenuto delle direttive con ambiti isolati vengono valutati nel contesto dell'ambito isolato. gumball non esiste nell'isolamento isolato, quindi tutto risulta non definito.

Sono disponibili due opzioni per risolvere il problema: (1) rimuovere l'ambito isolato (ad esempio scope: true per creare un ambito secondario); o (2) associare i valori nel proprio ambito isolato.

Per associare i tuoi attributi alle variabili di ambito, è sufficiente specificare la portata e il tipo di voi vincolante desiderare:

scope: { 
    id: '@', 
    color: '@' 
}, 

Questo dice che gli attributi id e color devono essere interpolato nel contesto di l'ambito genitore e quindi aggiunto all'ambito. Puoi rimuovere tutta quella logica all'interno della tua funzione link - questo lo farà per te.

Ma questo lascia ancora il problema del contenuto all'interno della direttiva. Per interpolare che, nel contesto del campo di applicazione genitore, è necessario inclusione:

transclude: true, 
template: "<div class='gumballColor{{color}}' ng-transclude></div>" 

Transclusion prende il contenuto dell'elemento e interpola relativa ad un nuovo figlio del campo di applicazione genitore, per esempio dove gumball sarebbe ancora definito.

Con queste due modifiche, la direttiva funzionerà come desiderato.

Se siete confusi su quale campo di applicazione da utilizzare, ecco un altro SO domanda che potrebbe aiutare: When writing a directive, how do I decide if a need no new scope, a new child scope, or a new isolate scope?


Nota a margine: Anche senza l'ambito isolato, la logica nella funzione link per determinare i valori degli attributi non funzionerebbero. L'ordine di esecuzione è la parte importante qui, che è approssimativamente: compilatore -> controllore -> collegamento -> interpolazione. Fino a quando l'interpolazione non viene eseguita, non c'è alcun valore per i tuoi attributi. Quindi i tuoi assegni non funzioneranno.

Detto questo, è possibile impostare uno $observe su attributi interpolati; lo $observe verrà sempre attivato la prima volta, anche se non è stato superato alcun valore. Puoi usarlo per impostare il tuo valore predefinito. $observe è anche molto efficiente.

attrs.$observe('attr1', function(val) { 
    if (!angular.isDefined(val)) { 
    scope.attr1 = 'defaultValue'; 
    } 
}); 
+0

+1 per il trucco del valore di default $. –

Problemi correlati