AngularJS: ng-yineleme listesi, model dizisinden bir model öğesi eklendiğinde güncellenmez


100

İki denetleyicim var ve aralarında bir app.factory işlevi ile veri paylaşıyorum.

İlk denetleyici, bir bağlantı tıklandığında model dizisine (pluginsDisplayed) bir pencere öğesi ekler. Parçacık diziye aktarılır ve bu değişiklik görünüme yansıtılır (dizi içeriğini göstermek için ng-yinelemeyi kullanır):

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

Widget üç yönerge üzerine inşa edilmiştir: k2plugin, remove ve resize. Remove yönergesi, k2plugin yönergesinin şablonuna bir yayılma alanı ekler. Bahsedilen span tıklandığında, paylaşılan dizideki doğru eleman ile silinir Array.splice(). Paylaşılan dizi doğru güncellenir, ama değişim olduğu değil görünümüne yansıtılması. Ancak, başka bir öğe eklendiğinde, kaldırma işleminden sonra görünüm doğru bir şekilde yenilenir ve önceden silinmiş öğe gösterilmez.

Neyi yanlış anlıyorum? Bunun neden işe yaramadığını bana açıklayabilir misin? AngularJS ile yapmaya çalıştığım şeyi yapmanın daha iyi bir yolu var mı?

Bu benim index.html'im:

<!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>

Bu benim 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);
        })
    };
});

Yanıtlar:


131

AngularJS dışında jQuery ile Ajax çağrısı yapmak veya buradaki gibi bir öğeye bir olay bağlamak gibi bir işlem yaptığınızda, AngularJS'nin kendisini güncellemesini bilmesine izin vermeniz gerekir. İşte yapmanız gereken kod değişikliği:

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

Bununla ilgili belgeler: https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$apply


4
$ Apply'a iletilen bir ifade / işlevin içine kapsam.remove (öğe) ve kapsam.resize (öğe) 'yi taşımayı düşünün.
Hornshøj-Schierbeck başına

1
@ PerHornshøj-Schierbeck Kabul ediyorum, aksi takdirde Angular bu hatalar meydana gelirse hatalardan haberdar olmayacak.
Jim Aho

2
dikkatli ol ! normalde, Angular gerektiğinde özet döngüsünü çağırır ve $ apply onu manuel olarak çağırır. Bunu manuel olarak adlandırmak genellikle kötü bir uygulamadır çünkü optimizasyon hataları yapabiliriz ve kaynak tüketebilir.
Alex

Oraya koyduğunuz kaldırma veya yeniden boyutlandırma işleminin ne olduğunu anlamıyorum.
Desarrollo Desafio de Guerrero

53

Ardından $scope.$apply();sağa eklerseniz $scope.pluginsDisplayed.splice(index,1);çalışır.

Bunun neden olduğundan emin değilim, ancak temelde AngularJS $ kapsamının değiştiğini bilmediğinde, $ apply'ı manuel olarak çağırmayı gerektirir. Ben de AngularJS'de yeniyim, bu yüzden bunu daha iyi açıklayamam. Ben de daha çok incelemeye ihtiyacım var.

Bunu oldukça doğru bir şekilde açıklayan bu harika makaleyi buldum . Not: Sanırım "fareyi indirmeye" bağlamak yerine ng-tıklama (dokümanlar) kullanmanın daha iyi olacağını düşünüyorum . Burada AngularJS'ye dayalı basit bir uygulama yazdım ( http://avinash.me/losh , kaynak http://github.com/hardfire/losh ). Çok temiz değil ama yardımcı olabilir.


7

Ben de aynı sorunu yaşadım. Sorun, 'ng-denetleyici'nin iki kez tanımlanmış olmasından kaynaklanıyordu (yönlendirmede ve ayrıca HTML'de).



0

Bunu yapmanın kolay bir yolu var. Çok kolay. Bunu fark ettiğimden beri

$scope.yourModel = [];

bu şekilde yapabileceğiniz tüm $ kapsam.yourModel dizi listesini kaldırır

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

$cope.yourModel, clonedObjects ile güncellenecektir.

Umarım yardımcı olur.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.