AngularJS: รายการ ng-repeat ไม่ได้รับการอัพเดตเมื่อองค์ประกอบโมเดลถูกต่อจากอาร์เรย์แบบจำลอง


100

ฉันมีคอนโทรลเลอร์สองตัวและแชร์ข้อมูลระหว่างกันด้วยฟังก์ชัน app.factory

คอนโทรลเลอร์ตัวแรกจะเพิ่มวิดเจ็ตในโมเดลอาร์เรย์ (pluginsDisplayed) เมื่อมีการคลิกลิงก์ วิดเจ็ตถูกผลักเข้าไปในอาร์เรย์และการเปลี่ยนแปลงนี้จะสะท้อนให้เห็นในมุมมอง (ซึ่งใช้ ng-repeat เพื่อแสดงเนื้อหาอาร์เรย์):

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

วิดเจ็ตสร้างขึ้นจากคำสั่งสามคำสั่ง k2plugin ลบและปรับขนาด คำสั่งการลบจะเพิ่มช่วงให้กับเทมเพลตของคำสั่ง k2plugin Array.splice()เมื่อกล่าวว่าช่วงที่มีการคลิกองค์ประกอบที่เหมาะสมลงในอาร์เรย์ที่ใช้ร่วมกันจะถูกลบออกด้วย อาร์เรย์ที่ใช้ร่วมกันได้รับการอัปเดตอย่างถูกต้อง แต่การเปลี่ยนแปลงจะไม่ปรากฏในมุมมอง อย่างไรก็ตามเมื่อเพิ่มองค์ประกอบอื่นหลังจากการลบมุมมองจะได้รับการรีเฟรชอย่างถูกต้องและองค์ประกอบที่ลบไปก่อนหน้านี้จะไม่แสดง

ฉันผิดอะไร คุณช่วยอธิบายฉันได้ไหมว่าทำไมถึงใช้ไม่ได้? มีวิธีที่ดีกว่าในการทำสิ่งที่ฉันพยายามทำกับ AngularJS หรือไม่?

นี่คือ 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>

นี่คือ 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);
        })
    };
});

คำตอบ:


131

เมื่อใดก็ตามที่คุณดำเนินการบางรูปแบบนอก AngularJS เช่นการโทร Ajax ด้วย jQuery หรือผูกเหตุการณ์กับองค์ประกอบเช่นที่คุณมีที่นี่คุณต้องแจ้งให้ AngularJS ทราบเพื่ออัปเดตตัวเอง นี่คือการเปลี่ยนแปลงรหัสที่คุณต้องทำ:

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

นี่คือเอกสารประกอบ: https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$apply


4
พิจารณาการย้ายขอบเขตลบ (องค์ประกอบ) และขอบเขตขนาด (องค์ประกอบ) ภายในนิพจน์ / ฟังก์ชันที่ส่งไปยัง $ apply
Per Hornshøj-Schierbeck

1
@ PerHornshøj-Schierbeck ฉันเห็นด้วยมิฉะนั้น Angular จะไม่ตระหนักถึงข้อผิดพลาดหากเกิดขึ้น
Jim Aho

2
ระวัง ! โดยปกติ Angular จะเรียกวงจรการย่อยเมื่อเขาต้องและ $ apply จะเรียกมันด้วยตนเอง มักจะเป็นแนวทางปฏิบัติที่ไม่ดีที่จะเรียกมันด้วยตนเองเนื่องจากเราสามารถสร้างข้อผิดพลาดในการเพิ่มประสิทธิภาพและอาจใช้ทรัพยากร
Alex

ฉันไม่เข้าใจว่าอะไรคือการลบหรือปรับขนาดที่คุณวางไว้ที่นั่น
Desarrollo Desafio de Guerrero

53

หากคุณเพิ่ม$scope.$apply();ทันทีหลังจาก$scope.pluginsDisplayed.splice(index,1);นั้นก็ใช้งานได้

ฉันไม่แน่ใจว่าเหตุใดจึงเกิดขึ้น แต่โดยพื้นฐานแล้วเมื่อ AngularJS ไม่รู้ว่าขอบเขต $ มีการเปลี่ยนแปลงต้องเรียก $ ใช้ด้วยตนเอง ฉันยังใหม่กับ AngularJS ดังนั้นจึงไม่สามารถอธิบายสิ่งนี้ได้ดีกว่านี้ ฉันต้องการดูให้มากขึ้นด้วย

ฉันพบบทความที่ยอดเยี่ยมนี้ซึ่งอธิบายได้ค่อนข้างถูกต้อง หมายเหตุ: ฉันคิดว่าการใช้ ng-click (docs)จะดีกว่าการผูกกับ "mousedown" ฉันเขียนแอพง่ายๆที่นี่ ( http://avinash.me/loshแหล่งที่มาhttp://github.com/hardfire/losh ) โดยอิงจาก AngularJS มันไม่ค่อยสะอาดนัก แต่อาจช่วยได้


7

ฉันมีปัญหาเดียวกัน ปัญหาเกิดจากการกำหนด "ng-controller" สองครั้ง (ในการกำหนดเส้นทางและใน HTML)



0

มีวิธีง่ายๆในการทำเช่นนั้น ง่ายมาก. เนื่องจากผมสังเกตว่า

$scope.yourModel = [];

ลบรายการอาร์เรย์ $ scope.yourModel ทั้งหมดที่คุณสามารถทำได้เช่นนี้

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 จะได้รับการอัพเดตด้วย clonedObjects

หวังว่าจะช่วยได้

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.