7 votes

La directive AngularJS avec ng-repeat n'est pas rendue

Le problème est que je dois gérer une liste de boules de gomme qui est récupérée depuis un service. La directive que j'ai créée semble fonctionner lorsque je code en dur les éléments dans le HTML, mais lorsque j'essaie d'allouer dynamiquement les boules de gomme en utilisant un ng-repeat.

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/

Les attributs id et color, lorsqu'ils sont passés dans la directive, finissent par être indéfinis lorsqu'ils sont passés à l'aide de ng-repeat, mais fonctionnent lorsqu'ils sont codés en dur dans le HTML.

10voto

Josh David Miller Points 66508

Le problème ici est votre portée d'isolement. En utilisant scope: {} vous avez créé une nouvelle portée, isolée, pour agir sur cet élément. Les portées isolées n'héritent pas de la portée parent. Tous les attributs et le contenu des directives avec des isolate scopes sont évalués dans le contexte de l'isolate scope. gumball n'existe pas dans la portée de l'isolat, donc tout est considéré comme indéfini.

Vous avez deux possibilités pour résoudre ce problème : (1) supprimer la portée de l'isolat (par exemple, l'isolate). scope: true pour créer une portée enfant) ; ou (2) lier les valeurs dans votre portée isolée.

Pour lier vos attributs à des variables de portée, il vous suffit de spécifier la portée et le type de liaison que vous souhaitez :

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

Cela signifie que les attributs id y color doivent être interpolés dans le contexte de l'étendue parent et ensuite ajoutés à l'étendue. Vous pouvez supprimer toute cette logique dans votre link qui le fera pour vous.

Mais cela laisse toujours le problème du contenu à l'intérieur de la directive. Pour interpoler cela dans le contexte de la portée parent, vous avez besoin de la transclusion :

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

La transclusion prend le contenu de l'élément et interpole par rapport à un nouvel enfant de la portée parentale, par exemple lorsque gumball serait toujours définie.

Avec ces deux changements, votre directive fonctionnera comme souhaité.

Si vous ne savez pas quelle lunette utiliser, voici une autre question de l'OS qui pourrait vous aider : Lorsque j'écris une directive, comment puis-je décider si je n'ai pas besoin d'une nouvelle portée, d'une nouvelle portée enfant ou d'une nouvelle portée isolée ?


Note complémentaire : Même sans la portée de l'isolat, la logique dans votre link pour déterminer les valeurs des attributs ne fonctionnerait pas. L'ordre d'exécution est la partie importante ici, qui est en gros : compilateur -> contrôleur -> liaison -> interpolation. Tant que l'interpolation n'est pas effectuée, il n'y a pas de valeur pour vos attributs. Donc vos contrôles ne fonctionneront pas.

Cela dit, vous pouvez mettre en place un $observe sur les attributs interpolés ; le $observe se déclenchera toujours la première fois, même si aucune valeur n'a été passée. Vous pouvez l'utiliser pour définir votre valeur par défaut. $observe est également très efficace.

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X