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, remove 및 resize라는 세 가지 지시문을 기반으로합니다. remove 지시문은 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

jQuery를 사용하여 Ajax 호출을 수행하거나 여기에있는 것과 같은 요소에 이벤트를 바인딩하는 것과 같이 AngularJS 외부에서 어떤 형태의 작업을 수행 할 때마다 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에 전달 된 표현식 / 함수 내에서 scope.remove (element) 및 scope.resize (element)를 이동하는 것을 고려하십시오.
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가 $ scope가 변경되었음을 알지 못하는 경우 $ apply를 수동으로 호출해야합니다. 나는 또한 AngularJS를 처음 접했기 때문에 이것을 더 잘 설명 할 수 없습니다. 나도 그것에 대해 더 살펴볼 필요가있다.

나는 그것을 아주 적절하게 설명하는 이 멋진 기사 를 찾았다 . 참고 : "mousedown"에 바인딩하는 것보다 ng-click (문서) 을 사용하는 것이 더 나을 것이라고 생각합니다 . AngularJS를 기반으로 여기에 간단한 앱 ( http://avinash.me/losh , 소스 http://github.com/hardfire/losh )을 작성했습니다. 매우 깨끗하지는 않지만 도움이 될 수 있습니다.


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.