AngularJS: daftar ng-repeat tidak diperbarui ketika elemen model disambung dari larik model

100

Saya memiliki dua pengontrol dan berbagi data di antara mereka dengan fungsi app.factory.

Pengontrol pertama menambahkan widget dalam larik model (pluginsDisplayed) saat tautan diklik. Widget didorong ke dalam array dan perubahan ini tercermin ke dalam tampilan (yang menggunakan ng-repeat untuk menampilkan konten array):

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

Widget ini dibangun di atas tiga arahan, k2plugin, hapus dan ubah ukuran. Perintah remove menambahkan rentang ke template direktif k2plugin. Ketika span tersebut diklik, elemen yang tepat ke dalam array bersama akan dihapus Array.splice(). Array bersama diperbarui dengan benar, tetapi perubahannya tidak tercermin dalam tampilan. Namun, ketika elemen lain ditambahkan, setelah dihapus, tampilan disegarkan dengan benar dan elemen yang sebelumnya dihapus tidak ditampilkan.

Apa yang salah? Bisakah Anda menjelaskan mengapa ini tidak berhasil? Apakah ada cara yang lebih baik untuk melakukan apa yang saya coba lakukan dengan AngularJS?

Ini adalah index.html saya:

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

Ini main.js saya:

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);
        })
    };
});
konferensi janes
sumber

Jawaban:

131

Setiap kali Anda melakukan beberapa bentuk operasi di luar AngularJS, seperti melakukan panggilan Ajax dengan jQuery, atau mengikat acara ke elemen seperti yang Anda miliki di sini, Anda perlu memberi tahu AngularJS untuk memperbarui dirinya sendiri. Berikut adalah perubahan kode yang perlu Anda lakukan:

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

Berikut dokumentasinya: https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$apply

Mathew Berg
sumber
4
Pertimbangkan untuk memindahkan scope.remove (elemen) dan scope.resize (elemen) di dalam ekspresi / fungsi yang diteruskan ke $ apply.
Per Hornshøj-Schierbeck
1
@ PerHornshøj-Schierbeck Saya setuju, jika tidak Angular tidak akan mengetahui kesalahan jika itu terjadi.
Jim Aho
2
hati-hati ! biasanya, Angular memanggil siklus intisari ketika dia harus dan $ apply akan memanggilnya secara manual. Seringkali merupakan praktik yang buruk untuk memanggilnya secara manual, karena kita dapat membuat kesalahan pengoptimalan, dan ini dapat menghabiskan sumber daya.
Alex
Saya tidak mengerti apa itu hapus atau ubah ukuran yang Anda taruh di sana.
Desarrollo Desafio de Guerrero
53

Jika Anda menambahkan $scope.$apply();hak setelahnya $scope.pluginsDisplayed.splice(index,1);maka itu berhasil.

Saya tidak yakin mengapa ini terjadi, tetapi pada dasarnya ketika AngularJS tidak tahu bahwa $ scope telah berubah, itu perlu memanggil $ apply secara manual. Saya juga baru mengenal AngularJS jadi tidak bisa menjelaskan ini lebih baik. Saya juga perlu melihat lebih dalam.

Saya menemukan artikel luar biasa ini yang menjelaskannya dengan cukup baik. Catatan: Saya pikir mungkin lebih baik menggunakan ng-click (docs) daripada mengikat ke "mousedown". Saya menulis aplikasi sederhana di sini ( http://avinash.me/losh , sumber http://github.com/hardfire/losh ) berdasarkan AngularJS. Memang tidak terlalu bersih, tapi mungkin bisa membantu.

avk
sumber
7

Saya memiliki masalah yang sama. Masalahnya adalah karena 'ng-controller' didefinisikan dua kali (dalam perutean dan juga dalam HTML).

shreedhar bhat
sumber
1

Hapus "track by index" dari ng-repeat dan itu akan menyegarkan DOM

Bassem Zaitoun
sumber
0

Ada cara mudah untuk melakukannya. Sangat mudah. Sejak saya menyadarinya

$scope.yourModel = [];

menghapus semua daftar array $ scope.yourModel yang bisa Anda lakukan seperti ini

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

$ Scope.yourModel akan diperbarui dengan clonedObjects.

Semoga membantu.

pengguna3856437
sumber