Programing

AngularJS 지시문은 범위 변수 변경시 업데이트되지 않습니다.

crosscheck 2020. 11. 14. 09:53
반응형

AngularJS 지시문은 범위 변수 변경시 업데이트되지 않습니다.


다른 템플릿 파일로 내용을 래핑하기 위해 작은 지시문을 작성하려고했습니다.

이 코드 :

<layout name="Default">My cool content</layout>

다음 출력이 있어야합니다.

<div class="layoutDefault">My cool content</div>

레이아웃 "Default"에는 다음 코드가 있기 때문입니다.

<div class="layoutDefault">{{content}}</div>

다음은 지시문의 코드입니다.

app.directive('layout', function($http, $compile){
return {
    restrict: 'E',
    link: function(scope, element, attributes) {
        var layoutName = (angular.isDefined(attributes.name)) ? attributes.name : 'Default';
        $http.get(scope.constants.pathLayouts + layoutName + '.html')
            .success(function(layout){
                var regexp = /^([\s\S]*?){{content}}([\s\S]*)$/g;
                var result = regexp.exec(layout);

                var templateWithLayout = result[1] + element.html() + result[2];
                element.html($compile(templateWithLayout)(scope));
            });
    }
}

});

내 문제:

템플릿 (레이아웃 템플릿 또는 레이아웃 태그 내부)에서 범위 변수를 사용할 때 {{whatever}}처음에는 작동합니다. whatever변수를 업데이트하면 지시문이 더 이상 업데이트되지 않습니다. 전체 링크 기능은 한 번만 트리거됩니다.

AngularJS는이 지시문이 범위 변수를 사용하므로 업데이트되지 않는다는 것을 알지 못합니다. 하지만이 동작을 수정하는 방법에 대한 단서가 없습니다.


바인딩 된 범위 변수를 만들고 변경 사항을 확인해야합니다.

return {
   restrict: 'E',
   scope: {
     name: '='
   },
   link: function(scope) {
     scope.$watch('name', function() {
        // all the code here...
     });
   }
};

이 문제에 대한 해결책도 필요했고이 스레드의 답변을 사용하여 다음을 생각해 냈습니다.

.directive('tpReport', ['$parse', '$http', '$compile', '$templateCache', function($parse, $http, $compile, $templateCache)
    {
        var getTemplateUrl = function(type)
        {
            var templateUrl = '';

            switch (type)
            {
                case 1: // Table
                    templateUrl = 'modules/tpReport/directives/table-report.tpl.html';
                    break;
                case 0:
                    templateUrl = 'modules/tpReport/directives/default.tpl.html';
                    break;
                default:
                    templateUrl = '';
                    console.log("Type not defined for tpReport");
                    break;
            }

            return templateUrl;
        };

        var linker = function (scope, element, attrs)
        {

            scope.$watch('data', function(){
                var templateUrl = getTemplateUrl(scope.data[0].typeID);
                var data = $templateCache.get(templateUrl);
                element.html(data);
                $compile(element.contents())(scope);

            });



        };

        return {
            controller: 'tpReportCtrl',
            template: '<div>{{data}}</div>',
            // Remove all existing content of the directive.
            transclude: true,
            restrict: "E",
            scope: {
                data: '='
            },
            link: linker
        };
    }])
    ;

HTML에 포함 :

<tp-report data='data'></tp-report>

이 지시문은 서버에서 검색된 데이터 세트를 기반으로 보고서 템플릿을 동적으로로드하는 데 사용됩니다.

scope.data 속성에 감시를 설정하고 업데이트 될 때마다 (사용자가 서버에서 새 데이터 세트를 요청할 때) 해당 지시문을로드하여 데이터를 표시합니다.


지시문이 범위 변수를 사용한다고 Angular에 알려야합니다.

범위의 일부 속성을 지시문에 바인딩해야합니다.

return {
    restrict: 'E',
    scope: {
      whatever: '='
    },
   ...
}

그리고 $watch그것은 :

  $scope.$watch('whatever', function(value) {
    // do something with the new value
  });

자세한 정보 는 지시문에 대한 Angular 문서 를 참조하십시오.


훨씬 더 나은 해결책을 찾았습니다.

app.directive('layout', function(){
    var settings = {
        restrict: 'E',
        transclude: true,
        templateUrl: function(element, attributes){
            var layoutName = (angular.isDefined(attributes.name)) ? attributes.name : 'Default';
            return constants.pathLayouts + layoutName + '.html';
        }
    }
    return settings;
});

현재 내가 보는 유일한 단점은 transcluded 템플릿이 자신의 범위를 가지고 있다는 사실입니다. 부모로부터 값을 가져 오지만 부모의 값을 변경하는 대신 값이 고유 한 새 자식 범위에 저장됩니다. 이를 방지하기 위해 이제 $parent.whatever대신 whatever.

예:

<layout name="Default">
    <layout name="AnotherNestedLayout">
        <label>Whatever:</label>
        <input type="text" ng-model="$parent.whatever">
    </layout>
</layout>

스코프에주의를 기울여야합니다.

방법은 다음과 같습니다.

<layout layoutId="myScope"></layout>

지침은 다음과 같아야합니다.

app.directive('layout', function($http, $compile){
    return {
        restrict: 'E',
        scope: {
            layoutId: "=layoutId"
        },
        link: function(scope, element, attributes) {
            var layoutName = (angular.isDefined(attributes.name)) ? attributes.name : 'Default';
            $http.get(scope.constants.pathLayouts + layoutName + '.html')
                .success(function(layout){
                    var regexp = /^([\s\S]*?){{content}}([\s\S]*)$/g;
                    var result = regexp.exec(layout);

                    var templateWithLayout = result[1] + element.html() + result[2];
                    element.html($compile(templateWithLayout)(scope));
        });
    }
}

$scope.$watch('myScope',function(){
        //Do Whatever you want
    },true)

마찬가지로 디렉티브에서 모델링 할 수 있으므로 모델이 자동으로 업데이트되면 watch 메서드가 디렉티브를 업데이트합니다.


나는 이것이 오래된 주제라는 것을 알고 있지만 누군가가 이것을 나처럼 발견하는 경우 :

"부모 범위"가 업데이트 될 때 값을 업데이트하기 위해 지시문이 필요할 때 다음 코드를 사용했습니다. 내가 아직 각도를 배우고 있기 때문에 잘못하고 있다면 꼭 정정하십시오.하지만 이것은 내가 필요한 것을했습니다.

지령:

directive('dateRangePrint', function(){
    return {
        restrict: 'E',
        scope:{
        //still using the single dir binding
            From: '@rangeFrom',
            To: '@rangeTo',
            format: '@format'
        },
        controller: function($scope, $element){

            $scope.viewFrom = function(){
                    return formatDate($scope.From, $scope.format);
                }

            $scope.viewTo = function(){
                    return formatDate($scope.To, $scope.format);
                }

            function formatDate(date, format){
                format = format || 'DD-MM-YYYY';

                //do stuff to date...

                return date.format(format);
            }

        },
        replace: true,
        // note the parenthesis after scope var
        template: '<span>{{ viewFrom() }} - {{ viewTo() }}</span>'
    }
})

우리는 이것을 시도 할 수 있습니다

$scope.$apply(function() {
    $scope.step1 = true;
    //scope.list2.length = 0;
});

http://jsfiddle.net/Etb9d/


A simple solution is to make the scope variable object. Then access the content with {{ whatever-object.whatever-property }}. The variable is not updating because JavaScript pass Primitive type by value. Whereas Object are passed by reference which solves the problem.


I am not sure why no one has yet suggested bindToController which removes all these ugly scopes and $watches. If You are using Angular 1.4

Below is a sample DOM:

<div ng-app="app">
    <div ng-controller="MainCtrl as vm">
        {{ vm.name }}
        <foo-directive name="vm.name"></foo-directive>
        <button ng-click="vm.changeScopeValue()">
        changeScopeValue
        </button>
    </div>
</div>

Follows the controller code:

angular.module('app', []);

// main.js
function MainCtrl() {
    this.name = 'Vinoth Initial';
    this.changeScopeValue = function(){
        this.name = "Vinoth has Changed"
    }
}

angular
    .module('app')
    .controller('MainCtrl', MainCtrl);

// foo.js
function FooDirCtrl() {
}

function fooDirective() {
    return {
        restrict: 'E',
        scope: {
            name: '='
        },
        controller: 'FooDirCtrl',
        controllerAs: 'vm',
        template:'<div><input ng-model="name"></div>',
        bindToController: true
    };
}

angular
    .module('app')
    .directive('fooDirective', fooDirective)
    .controller('FooDirCtrl', FooDirCtrl);

A Fiddle to play around, here we are changing the scope value in the controller and automatically the directive updates on scope change. http://jsfiddle.net/spechackers/1ywL3fnq/

참고URL : https://stackoverflow.com/questions/20068526/angularjs-directive-does-not-update-on-scope-variable-changes

반응형