AngularJS: danh sách ng-repeat không được cập nhật khi một phần tử mô hình được nối từ mảng mô hình


100

Tôi có hai bộ điều khiển và chia sẻ dữ liệu giữa chúng với một chức năng app.factory.

Bộ điều khiển đầu tiên thêm một tiện ích con trong mảng mô hình (pluginsDisplayed) khi một liên kết được nhấp vào. Tiện ích con được đẩy vào mảng và thay đổi này được phản ánh trong chế độ xem (sử dụng ng-repeat để hiển thị nội dung mảng):

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

Tiện ích được xây dựng dựa trên ba chỉ thị, k2plugin, xóa và thay đổi kích thước. Chỉ thị loại bỏ thêm một khoảng vào mẫu của chỉ thị k2plugin. Khi nhịp đã nói được nhấp, phần tử bên phải trong mảng được chia sẻ sẽ bị xóa với Array.splice(). Mảng được chia sẻ được cập nhật chính xác, nhưng thay đổi không được phản ánh trong chế độ xem. Tuy nhiên, khi một phần tử khác được thêm vào, sau khi xóa, chế độ xem được làm mới chính xác và phần tử đã xóa trước đó không được hiển thị.

Tôi đang làm gì sai? Bạn có thể giải thích cho tôi lý do tại sao điều này không hoạt động? Có cách nào tốt hơn để làm những gì tôi đang cố gắng làm với AngularJS không?

Đây là index.html của tôi:

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

Đây là main.js của tôi:

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

Câu trả lời:


131

Bất cứ khi nào bạn thực hiện một số hình thức hoạt động bên ngoài AngularJS, chẳng hạn như thực hiện lệnh gọi Ajax với jQuery hoặc liên kết một sự kiện với một phần tử như bạn có ở đây, bạn cần cho AngularJS biết để tự cập nhật. Đây là thay đổi mã bạn cần làm:

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

Đây là tài liệu về nó: https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$apply


4
Hãy cân nhắc việc di chuyển scope.remove (phần tử) và scope.resize (phần tử) vào bên trong một biểu thức / hàm được truyền cho $ apply.
Per Hornshøj-Schierbeck

1
@ PerHornshøj-Schierbeck Tôi đồng ý, nếu không Angular sẽ không nhận thức được lỗi nếu những điều đó xảy ra.
Jim Aho

2
hãy cẩn thận ! thông thường, Angular gọi chu trình thông báo khi anh ta phải và $ áp dụng sẽ gọi nó theo cách thủ công. Việc gọi nó theo cách thủ công thường là một thói quen xấu vì chúng tôi có thể mắc lỗi tối ưu hóa và nó có thể tiêu tốn tài nguyên.
Alex

Tôi không hiểu loại bỏ hoặc thay đổi kích thước mà bạn đang đặt ở đó là gì.
Desarrollo Desafio de Guerrero

53

Nếu bạn thêm một $scope.$apply();quyền sau $scope.pluginsDisplayed.splice(index,1);đó thì nó hoạt động.

Tôi không chắc tại sao điều này lại xảy ra, nhưng về cơ bản khi AngularJS không biết rằng phạm vi $ đã thay đổi, nó yêu cầu gọi $ apply theo cách thủ công. Tôi cũng mới sử dụng AngularJS nên không thể giải thích điều này tốt hơn. Tôi cũng cần xem xét nhiều hơn vào nó.

Tôi tìm thấy bài viết tuyệt vời này giải thích nó khá đúng. Lưu ý: Tôi nghĩ có thể tốt hơn nếu sử dụng ng-click (docs) hơn là ràng buộc với "mousedown". Tôi đã viết một ứng dụng đơn giản ở đây ( http://avinash.me/losh , nguồn http://github.com/hardfire/losh ) dựa trên AngularJS. Nó không sạch lắm, nhưng nó có thể hữu ích.


7

Tôi gặp vấn đề tương tự. Vấn đề là do 'ng-controller' đã được định nghĩa hai lần (trong định tuyến và cả trong HTML).



0

Có một cách dễ dàng để làm điều đó. Rất dễ. Kể từ khi tôi nhận thấy rằng

$scope.yourModel = [];

loại bỏ tất cả danh sách mảng $ scope.yourModel mà bạn có thể làm như thế này

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 sẽ được cập nhật với clonedObjects.

Hy vọng rằng sẽ giúp.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.