web-dev-qa-db-fra.com

La directive AngularJS ne se met pas à jour en cas de modification des variables de portée

J'ai essayé d'écrire une petite directive, pour envelopper le contenu avec un autre fichier modèle.

Ce code:

<layout name="Default">My cool content</layout>

Devrait avoir cette sortie:

<div class="layoutDefault">My cool content</div>

Parce que la mise en page "Default" a ce code:

<div class="layoutDefault">{{content}}</div>

Voici le code de la directive:

app.directive('layout', function($http, $compile){
return {
    restrict: 'E',
    link: function(scope, element, attributes) {
        var layoutName = (angular.isDefined(attributes.name)) ? attributes.name : 'Default';
        $http.get(scope.constants.pathLayouts + layoutName + '.html')
            .success(function(layout){
                var regexp = /^([\s\S]*?){{content}}([\s\S]*)$/g;
                var result = regexp.exec(layout);

                var templateWithLayout = result[1] + element.html() + result[2];
                element.html($compile(templateWithLayout)(scope));
            });
    }
}

});

Mon problème:

Lorsque j'utilise des variables d'étendue dans un modèle (dans un modèle de présentation ou à l'intérieur d'une balise de présentation), par exemple. {{whatever}} ça marche juste au début. Si je mets à jour la variable whatever, la directive n'est plus mise à jour. Toute la fonction de liaison ne sera déclenchée qu'une fois.

Je pense qu'AngularJS ne sait pas, que cette directive utilise des variables de portée et qu'elle ne sera donc pas mise à jour. Mais je ne sais pas comment résoudre ce problème.

65
Armin

Vous devez créer une variable de portée liée et surveiller ses modifications:

return {
   restrict: 'E',
   scope: {
     name: '='
   },
   link: function(scope) {
     scope.$watch('name', function() {
        // all the code here...
     });
   }
};
79
QuarK

J'avais également besoin d'une solution à ce problème et j'ai utilisé les réponses fournies dans ce fil pour proposer ce qui suit:

.directive('tpReport', ['$parse', '$http', '$compile', '$templateCache', function($parse, $http, $compile, $templateCache)
    {
        var getTemplateUrl = function(type)
        {
            var templateUrl = '';

            switch (type)
            {
                case 1: // Table
                    templateUrl = 'modules/tpReport/directives/table-report.tpl.html';
                    break;
                case 0:
                    templateUrl = 'modules/tpReport/directives/default.tpl.html';
                    break;
                default:
                    templateUrl = '';
                    console.log("Type not defined for tpReport");
                    break;
            }

            return templateUrl;
        };

        var linker = function (scope, element, attrs)
        {

            scope.$watch('data', function(){
                var templateUrl = getTemplateUrl(scope.data[0].typeID);
                var data = $templateCache.get(templateUrl);
                element.html(data);
                $compile(element.contents())(scope);

            });



        };

        return {
            controller: 'tpReportCtrl',
            template: '<div>{{data}}</div>',
            // Remove all existing content of the directive.
            transclude: true,
            restrict: "E",
            scope: {
                data: '='
            },
            link: linker
        };
    }])
    ;

Inclure dans votre html:

<tp-report data='data'></tp-report>

Cette directive est utilisée pour charger dynamiquement des modèles de rapport en fonction du jeu de données extrait du serveur.

Il active la surveillance de la propriété scope.data et, chaque fois que celle-ci est mise à jour (lorsque les utilisateurs demandent un nouveau jeu de données au serveur), il charge la directive correspondante pour afficher les données.

41
Roy Milder

Vous devez dire à Angular que votre directive utilise une variable de portée:

Vous devez lier certaines propriétés de la portée à votre directive:

return {
    restrict: 'E',
    scope: {
      whatever: '='
    },
   ...
}

puis $watch le:

  $scope.$watch('whatever', function(value) {
    // do something with the new value
  });

Reportez-vous à la Documentation angulaire sur les directives pour plus d'informations.

16
Paul Mougel

J'ai trouvé une bien meilleure solution:

app.directive('layout', function(){
    var settings = {
        restrict: 'E',
        transclude: true,
        templateUrl: function(element, attributes){
            var layoutName = (angular.isDefined(attributes.name)) ? attributes.name : 'Default';
            return constants.pathLayouts + layoutName + '.html';
        }
    }
    return settings;
});

Le seul inconvénient que je vois actuellement est le fait que les modèles inclus ont leur propre portée. Ils obtiennent les valeurs de leurs parents, mais au lieu de changer la valeur dans le parent, la valeur est stockée dans une nouvelle portée enfant. Pour éviter cela, j'utilise maintenant $parent.whatever au lieu de whatever.

Exemple:

<layout name="Default">
    <layout name="AnotherNestedLayout">
        <label>Whatever:</label>
        <input type="text" ng-model="$parent.whatever">
    </layout>
</layout>
8
Armin

Vous devriez garder un oeil sur votre portée.

Voici comment vous pouvez le faire:

<layout layoutId="myScope"></layout>

Votre directive devrait ressembler à

app.directive('layout', function($http, $compile){
    return {
        restrict: 'E',
        scope: {
            layoutId: "=layoutId"
        },
        link: function(scope, element, attributes) {
            var layoutName = (angular.isDefined(attributes.name)) ? attributes.name : 'Default';
            $http.get(scope.constants.pathLayouts + layoutName + '.html')
                .success(function(layout){
                    var regexp = /^([\s\S]*?){{content}}([\s\S]*)$/g;
                    var result = regexp.exec(layout);

                    var templateWithLayout = result[1] + element.html() + result[2];
                    element.html($compile(templateWithLayout)(scope));
        });
    }
}

$scope.$watch('myScope',function(){
        //Do Whatever you want
    },true)

De même, vous pouvez insérer des modèles dans votre directive. Ainsi, si les mises à jour de modèles sont automatiquement mises à jour, votre méthode de surveillance mettra à jour votre directive.

2
Aditya Sethi

Je sais que c'est un sujet ancien, mais au cas où quelqu'un le trouverait comme moi:

J'ai utilisé le code suivant lorsque j'avais besoin de ma directive pour mettre à jour les valeurs lorsque la "portée parent" était mise à jour. S'il vous plaît par tous les moyens me corriger si je fais quelque chose de mal que je suis en train d'apprendre angulaire, mais cela a fait ce dont j'avais besoin;

directif: 

directive('dateRangePrint', function(){
    return {
        restrict: 'E',
        scope:{
        //still using the single dir binding
            From: '@rangeFrom',
            To: '@rangeTo',
            format: '@format'
        },
        controller: function($scope, $element){

            $scope.viewFrom = function(){
                    return formatDate($scope.From, $scope.format);
                }

            $scope.viewTo = function(){
                    return formatDate($scope.To, $scope.format);
                }

            function formatDate(date, format){
                format = format || 'DD-MM-YYYY';

                //do stuff to date...

                return date.format(format);
            }

        },
        replace: true,
        // note the parenthesis after scope var
        template: '<span>{{ viewFrom() }} - {{ viewTo() }}</span>'
    }
})
1
slappy-x

On peut essayer ça

$scope.$apply(function() {
    $scope.step1 = true;
    //scope.list2.length = 0;
});

http://jsfiddle.net/Etb9d/

0
build-1

Une solution simple consiste à rendre la variable de portée object . Accédez ensuite au contenu avec {{ whatever-object.whatever-property }}. La variable ne se met pas à jour car JavaScript passe Primitive tapez par valeur . Alors que Objet sont passés par référence qui résout le problème.

0
Yohe