AngularJS: Список ng-повторів не оновлюється, коли елемент моделі зрощено з масиву моделі


100

У мене є два контролери і ділитися даними між ними за допомогою функції app.factory.

Перший контролер додає віджет у масив моделі (плагіни відображається) при натисканні на посилання. Віджет висувається в масив, і ця зміна відображається у представленні (для використання вмісту масиву використовується ng-повтор):

<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
Поміркуйте над переміщенням names.remove (елемент) і domain.resize (елемент) всередині виразу / функції, переданого в $ apply.
Per Hornshøj-Schierbeck

1
@ PerHornshøj-Schierbeck Я згоден, інакше Angular не буде знати про помилки, якщо вони трапляються.
Джим Ахо

2
будь обережний ! як правило, кутовий цикл виклику кутів, коли він повинен і $ застосувати, викликає його вручну. Часто поганою практикою називати це вручну, оскільки ми можемо робити помилки оптимізації, і це може зайняти багато ресурсів.
Олексій

Я не розумію, що це таке, щоб видалити чи змінити розмір, який ви там поміщаєте.
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 = [];

видаляє весь список $ range.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;
}

$ Range.yourModel буде оновлено за допомогою clonedObjects.

Сподіваюся, що це допомагає.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.