AngularJS: ng-repeat list no se actualiza cuando un elemento de modelo se empalma de la matriz de modelo

Tengo dos controladores y comparto datos entre ellos con una función app.factory.

El primer controlador agrega un widget en la matriz modelo (pluginsDisplayed) cuando se hace clic en un enlace. El widget se inserta en la matriz y este cambio se refleja en la vista (que usa ng-repeat para mostrar el contenido de la matriz):

El widget se basa en tres directivas, k2plugin, eliminar y cambiar el tamaño. La directiva remove agrega un tramo a la plantilla de la directiva k2plugin. Cuando se hace clic en dicho intervalo, el elemento correcto en el conjunto compartido se elimina con Array.splice() . La matriz compartida se actualiza correctamente, pero el cambio no se refleja en la vista. Sin embargo, cuando se agrega otro elemento, después de la eliminación, la vista se actualiza correctamente y no se muestra el elemento eliminado previamente.

¿Qué me estoy equivocando? ¿Podrías explicarme por qué esto no funciona? ¿Hay una mejor manera de hacer lo que estoy tratando de hacer con AngularJS?

Este es mi index.html:

         
Add one of {{pluginList.length}} plugins
  • {{plugin.name}}
  • Este es mi 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: "
    {{pluginname}} _ X" + "
    Plugin 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); }) }; });

    Cada vez que realice alguna operación fuera de AngularJS, como realizar una llamada Ajax con jQuery, o vincular un evento a un elemento como el que tiene aquí, debe informar a AngularJS para que se actualice solo. Aquí está el cambio de código que debe hacer:

     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(); }) }; }); 

    Aquí está la documentación: https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$apply

    Si agrega un $scope.$apply(); justo después de $scope.pluginsDisplayed.splice(index,1); entonces funciona

    No estoy seguro de por qué sucede esto, pero básicamente cuando AngularJS no sabe que el $ scope ha cambiado, se requiere llamar $ apply manualmente. También soy nuevo en AngularJS así que no puedo explicar esto mejor. También necesito mirar más adentro.

    Encontré este asombroso artículo que lo explica bastante bien. Nota: Creo que sería mejor usar ng-click (docs) en lugar de vincularse a “mousedown”. Escribí una aplicación simple aquí ( http://avinash.me/losh , fuente http://github.com/hardfire/losh ) basada en AngularJS. No está muy limpio, pero podría ser de ayuda.

    Tuve el mismo problema. El problema era porque ‘ng-controller’ se definió dos veces (en el enrutamiento y también en el HTML).

    Hay una manera fácil de hacer eso. Muy fácil. Desde que noté eso

     $scope.yourModel = []; 

    elimina toda la lista de arreglos de $ scope.yourModel que puede hacer de esta manera

     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; } 

    El $ scope.yourModel se actualizará con clonedObjects.

    Espero que ayude.

    Eliminar “seguimiento por índice” de ng-repeat y actualizará el DOM