web-dev-qa-db-fra.com

AngularJS: la liste ng-repeat n'est pas mise à jour lorsqu'un élément de modèle est épissé à partir du tableau de modèle

J'ai deux contrôleurs et partage des données entre eux avec une fonction app.factory.

Le premier contrôleur ajoute un widget dans le tableau de modèles (pluginsDisplayed) lorsqu'un clic est effectué sur un lien. Le widget est poussé dans le tableau et cette modification est reflétée dans la vue (qui utilise ng-repeat pour afficher le contenu du tableau):

<div ng-repeat="pluginD in pluginsDisplayed">
    <div k2plugin pluginname="{{pluginD.name}}" pluginid="{{pluginD.id}}"></div>
</div>

Le widget est construit sur trois directives, k2plugin, remove et resize. La directive remove ajoute une étendue au modèle de la directive k2plugin. Lorsque l'on clique sur cette étendue, le bon élément du tableau partagé est supprimé avec Array.splice(). Le tableau partagé est correctement mis à jour, mais la modification est pas reflétée dans la vue. Cependant, lorsqu'un autre élément est ajouté, après la suppression, la vue est actualisée correctement et l'élément précédemment supprimé n'est pas affiché.

Qu'est-ce que je me trompe? Pourriez-vous m'expliquer pourquoi cela ne fonctionne pas? Existe-t-il une meilleure façon de faire ce que j'essaie de faire avec AngularJS?

Ceci est mon index.html:

<!doctype html>
<html>
    <head>
        <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.5/angular.min.js">
        </script>
        <script src="main.js"></script>
    </head>
    <body>
        <div ng-app="livePlugins">
            <div ng-controller="pluginlistctrl">
                <span>Add one of {{pluginList.length}} plugins</span>
                <li ng-repeat="plugin in pluginList">
                    <span><a href="" ng-click="add()">{{plugin.name}}</a></span>
                </li>
            </div>
            <div ng-controller="k2ctrl">
                <div ng-repeat="pluginD in pluginsDisplayed">
                    <div k2plugin pluginname="{{pluginD.name}}" pluginid="{{pluginD.id}}"></div>
                </div>
            </div>
        </div>
    </body>
</html>

Ceci est mon main.js:

var app = angular.module ("livePlugins",[]);

app.factory('Data', function () {
    return {pluginsDisplayed: []};
});

app.controller ("pluginlistctrl", function ($scope, Data) {
    $scope.pluginList = [{name: "plugin1"}, {name:"plugin2"}, {name:"plugin3"}];
    $scope.add = function () {
        console.log ("Called add on", this.plugin.name, this.pluginList);
        var newPlugin = {};
        newPlugin.id = this.plugin.name + '_'  + (new Date()).getTime();
        newPlugin.name = this.plugin.name;
        Data.pluginsDisplayed.Push (newPlugin);
    }
})

app.controller ("k2ctrl", function ($scope, Data) {
    $scope.pluginsDisplayed = Data.pluginsDisplayed;

    $scope.remove = function (element) {
        console.log ("Called remove on ", this.pluginid, element);

        var len = $scope.pluginsDisplayed.length;
        var index = -1;

        // Find the element in the array
        for (var i = 0; i < len; i += 1) {
            if ($scope.pluginsDisplayed[i].id === this.pluginid) {
                index = i;
                break;
            }
        }

        // Remove the element
        if (index !== -1) {
            console.log ("removing the element from the array, index: ", index);
            $scope.pluginsDisplayed.splice(index,1);
        }

    }
    $scope.resize = function () {
        console.log ("Called resize on ", this.pluginid);
    }
})

app.directive("k2plugin", function () {
    return {
        restrict: "A",
        scope: true,
        link: function (scope, elements, attrs) {
            console.log ("creating plugin");

            // This won't work immediately. Attribute pluginname will be undefined
            // as soon as this is called.
            scope.pluginname = "Loading...";
            scope.pluginid = attrs.pluginid;

            // Observe changes to interpolated attribute
            attrs.$observe('pluginname', function(value) {
                console.log('pluginname has changed value to ' + value);
                scope.pluginname = attrs.pluginname;
            });

            // Observe changes to interpolated attribute
            attrs.$observe('pluginid', function(value) {
                console.log('pluginid has changed value to ' + value);
                scope.pluginid = attrs.pluginid;
            });
        },
        template: "<div>{{pluginname}} <span resize>_</span> <span remove>X</span>" +
                       "<div>Plugin DIV</div>" +
                  "</div>",
        replace: true
    };
});

app.directive("remove", function () {
    return function (scope, element, attrs) {
        element.bind ("mousedown", function () {
            scope.remove(element);
        })
    };

});

app.directive("resize", function () {
    return function (scope, element, attrs) {
        element.bind ("mousedown", function () {
            scope.resize(element);
        })
    };
});
101
janesconference

Chaque fois que vous effectuez une opération quelconque en dehors d’AngularJS, telle que passer un appel Ajax avec jQuery ou lier un événement à un élément comme vous l’avez fait ici, vous devez laisser savoir à AngularJS de se mettre à jour. Voici le changement de code que vous devez faire:

app.directive("remove", function () {
    return function (scope, element, attrs) {
        element.bind ("mousedown", function () {
            scope.remove(element);
            scope.$apply();
        })
    };

});

app.directive("resize", function () {
    return function (scope, element, attrs) {
        element.bind ("mousedown", function () {
            scope.resize(element);
            scope.$apply();
        })
    };
});

Voici la documentation à ce sujet: https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$apply

132
Mathew Berg

Si vous ajoutez une $scope.$apply(); juste après $scope.pluginsDisplayed.splice(index,1);, alors cela fonctionne.

Je ne sais pas pourquoi cela se produit, mais si AngularJS ne sait pas que la portée de $ a changé, il faut appeler $ apply manuellement. Je suis aussi nouveau sur AngularJS, je ne peux donc pas mieux l'expliquer. J'ai aussi besoin de regarder plus dedans.

J'ai trouvé cet article génial qui l'explique assez correctement. Note: Je pense qu'il serait peut-être préférable d'utiliser ng-click (docs) plutôt que de se lier à "mousedown". J'ai écrit une application simple ici ( http://avinash.me/losh , source http://github.com/hardfire/losh ) basée sur AngularJS. Ce n'est pas très propre, mais cela pourrait être utile.

54
avk

J'ai eu le même problème. Le problème était que 'ng-controller' a été défini deux fois (dans le routage et aussi dans le HTML).

7
shreedhar bhat

Il y a un moyen facile de faire ça. Très facile. Depuis que j'ai remarqué que

$scope.yourModel = [];

supprime toute la liste de tableaux $ scope.yourModel que vous pouvez faire comme ceci

function deleteAnObjectByKey(objects, key) {
    var clonedObjects = Object.assign({}, objects);

     for (var x in clonedObjects)
        if (clonedObjects.hasOwnProperty(x))
             if (clonedObjects[x].id == key)
                 delete clonedObjects[x];

    $scope.yourModel = clonedObjects;
}

Le $ scope.yourModel sera mis à jour avec les clonedObjects.

J'espère que ça t'as aidé.

0
user3856437

Supprimez "piste par index" du ng-repeat et cela rafraîchirait le DOM

0
Bassem Zaitoun