wake-up-neo.com

AngularJS: Die ng-repeat-Liste wird nicht aktualisiert, wenn ein Modellelement aus dem Modellarray gespleißt wird

Ich habe zwei Controller und teile Daten zwischen ihnen mit einer App.Factory-Funktion.

Der erste Controller fügt dem Modellarray (pluginsDisplayed) ein Widget hinzu, wenn auf einen Link geklickt wird. Das Widget wird in das Array verschoben und diese Änderung wird in die Ansicht übernommen (die den Array-Inhalt mit ng-repeat anzeigt):

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

Das Widget basiert auf drei Direktiven: k2plugin, remove und resize. Die Anweisung remove fügt der Vorlage der Anweisung k2plugin einen Bereich hinzu. Wenn dieser Bereich angeklickt wird, wird das rechte Element im gemeinsam genutzten Array mit Array.splice() gelöscht. Das gemeinsam genutzte Array wird korrekt aktualisiert, aber die Änderung wird nicht in der Ansicht widergespiegelt . Wenn jedoch nach dem Entfernen ein anderes Element hinzugefügt wird, wird die Ansicht korrekt aktualisiert und das zuvor gelöschte Element wird nicht angezeigt.

Was mache ich falsch? Können Sie mir erklären, warum das nicht funktioniert? Gibt es einen besseren Weg, um das zu tun, was ich mit AngularJS versuche?

Das ist meine 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>

Das ist meine 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

Wenn Sie eine Operation außerhalb von AngularJS ausführen, z. B. einen Ajax-Aufruf mit jQuery ausführen oder ein Ereignis an ein Element wie das hier angegebene binden, müssen Sie AngularJS über die Aktualisierung informieren. Hier ist die Codeänderung, die Sie vornehmen müssen:

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

Hier ist die Dokumentation dazu: https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$apply

132
Mathew Berg

Wenn Sie $scope.$apply(); direkt nach $scope.pluginsDisplayed.splice(index,1); hinzufügen, funktioniert es.

Ich bin nicht sicher, warum dies passiert, aber wenn AngularJS nicht weiß, dass sich der $ -Bereich geändert hat, muss $ apply manuell aufgerufen werden. Ich bin auch neu in AngularJS und kann dies nicht besser erklären. Ich muss mich auch genauer darum kümmern.

Ich fand dieser fantastische Artikel das erklärt es ganz richtig. Hinweis: Ich denke, es ist besser, ng-click (docs) zu verwenden, als an "mousedown" zu binden. Ich habe hier eine einfache App geschrieben ( http://avinash.me/losh , Quelle http://github.com/hardfire/losh ) basierend auf AngularJS. Es ist nicht sehr sauber, aber es könnte hilfreich sein.

54
avk

Ich hatte das gleiche Problem. Das Problem war, dass 'ng-controller' zweimal definiert wurde (im Routing und auch im HTML).

7
shreedhar bhat

Es gibt einen einfachen Weg, das zu tun. Sehr leicht. Da ist mir das aufgefallen

$scope.yourModel = [];

entfernt alle $ scope.yourModel-Array-Listen, die Sie auf diese Weise erstellen können

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

Das $ scope.yourModel wird mit den clonedObjects aktualisiert.

Ich hoffe, das hilft.

0
user3856437

Entfernen Sie "track by index" aus dem ng-repeat und es wird das DOM aktualisiert

0
Bassem Zaitoun