AngularJS : 서비스 속성에 바인딩하는 올바른 방법



Answers

내 관점에서 보면 $watch 가 최선의 방법 일 것입니다.

실제로 예제를 약간 단순화 할 수 있습니다.

function TimerCtrl1($scope, Timer) {
  $scope.$watch( function () { return Timer.data; }, function (data) {
    $scope.lastUpdated = data.lastUpdated;
    $scope.calls = data.calls;
  }, true);
}

그게 당신이 필요한 전부입니다.

속성이 동시에 업데이트되므로 하나의 시계 만 있으면됩니다. 또한 그들은 하나의 작은 객체에서 Timer.data 때문에 Timer.data 속성을보기 위해이를 변경했습니다. $watch 전달 된 마지막 매개 변수는 참조가 동일 함을 보장하기보다는 깊은 평등을 검사하도록 지시합니다.

컨텍스트를 조금 제공하기 위해 서비스 값을 범위에 직접 두는 것이이 방법을 선호하는 이유는 적절한 관심의 분리를 보장하는 것입니다. 귀하의 견해가 운영되기 위해서는 귀하의 서비스에 대해 알 필요가 없습니다. 컨트롤러의 일은 모든 것을 아교로 연결하는 것입니다. 그것의 임무는 당신의 서비스로부터 데이터를 얻고 필요한 어떤 방법으로 그들을 처리 한 다음 당신이 필요로하는 것이 무엇이든지간에 당신의 견해를 제공하는 것입니다. 그러나 나는 그 일이 바로 그 견해에 따라 서비스를 전달하는 것이라고 생각하지 않는다. 그렇지 않으면 컨트롤러가 거기서 무엇을하고 있습니까? AngularJS 개발자는 템플리트에 "논리"를 포함하지 않기로 결정했을 때 동일한 추론을 따랐습니다 (예 : if 문).

공정하게 말하자면 여기에는 여러 가지 관점이있을 것입니다. 나는 다른 대답을 기대합니다.

Question

AngularJS에서 서비스 속성에 바인딩하는 가장 좋은 방법을 찾고 있습니다.

여러 예제를 통해 AngularJS를 사용하여 생성 된 서비스의 속성에 바인딩하는 방법을 이해했습니다.

아래에는 서비스의 속성에 바인딩하는 방법에 대한 두 가지 예가 나와 있습니다. 둘 다 작동합니다. 첫 번째 예제는 기본 바인딩을 사용하고 두 번째 예제는 $ scope를 사용했습니다. $ watch는 서비스 속성에 바인딩합니다.

이 예제 중 하나가 서비스의 속성에 바인딩 할 때 선호됩니까 아니면 내가 권장하지 않는 다른 옵션이 있습니까?

이 예제의 전제는 서비스가 "lastUpdated"및 "calls"속성을 5 초마다 업데이트해야한다는 것입니다. 서비스 등록 정보가 업데이트되면보기에는 이러한 변경 사항이 반영되어야합니다. 이 두 예제 모두 성공적으로 작동합니다. 나는 그것을하는 더 좋은 방법이 있는지 궁금합니다.

기본 바인딩

다음 코드를보고 실행할 수 있습니다. http://plnkr.co/edit/d3c16z

<html>
<body ng-app="ServiceNotification" >

    <div ng-controller="TimerCtrl1" style="border-style:dotted"> 
        TimerCtrl1 <br/>
        Last Updated: {{timerData.lastUpdated}}<br/>
        Last Updated: {{timerData.calls}}<br/>
    </div>

    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.5/angular.js"></script>
    <script type="text/javascript">
        var app = angular.module("ServiceNotification", []);

        function TimerCtrl1($scope, Timer) {
            $scope.timerData = Timer.data;
        };

        app.factory("Timer", function ($timeout) {
            var data = { lastUpdated: new Date(), calls: 0 };

            var updateTimer = function () {
                data.lastUpdated = new Date();
                data.calls += 1;
                console.log("updateTimer: " + data.lastUpdated);

                $timeout(updateTimer, 5000);
            };
            updateTimer();

            return {
                data: data
            };
        });
    </script>
</body>
</html>

다른 방법으로 서비스 속성에 바인딩하는 방법은 컨트롤러에서 $ scope. $ watch를 사용하는 것입니다.

$ scope. $ watch

다음 코드를보고 실행할 수 있습니다. http://plnkr.co/edit/dSBlC9

<html>
<body ng-app="ServiceNotification">
    <div style="border-style:dotted" ng-controller="TimerCtrl1">
        TimerCtrl1<br/>
        Last Updated: {{lastUpdated}}<br/>
        Last Updated: {{calls}}<br/>
    </div>

    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.5/angular.js"></script>
    <script type="text/javascript">
        var app = angular.module("ServiceNotification", []);

        function TimerCtrl1($scope, Timer) {
            $scope.$watch(function () { return Timer.data.lastUpdated; },
                function (value) {
                    console.log("In $watch - lastUpdated:" + value);
                    $scope.lastUpdated = value;
                }
            );

            $scope.$watch(function () { return Timer.data.calls; },
                function (value) {
                    console.log("In $watch - calls:" + value);
                    $scope.calls = value;
                }
            );
        };

        app.factory("Timer", function ($timeout) {
            var data = { lastUpdated: new Date(), calls: 0 };

            var updateTimer = function () {
                data.lastUpdated = new Date();
                data.calls += 1;
                console.log("updateTimer: " + data.lastUpdated);

                $timeout(updateTimer, 5000);
            };
            updateTimer();

            return {
                data: data
            };
        });
    </script>
</body>
</html>

나는 서비스에서 $ rootscope. $ 브로드 캐스트를 사용하고 컨트롤러에서 $ root. $를 사용할 수 있다는 것을 알고 있지만, 첫 번째 브로드 캐스트에서 $ broadcast / $를 사용하는 다른 예제에서는 컨트롤러에서 브로드 캐스트되는 추가 통화가 컨트롤러에서 트리거됩니다. $ rootscope. $ broadcast 문제를 해결하는 방법을 알고 있다면 대답을 제공해주십시오.

그러나 앞서 언급 한 내용을 다시 말하면 서비스 속성에 바인딩하는 가장 좋은 방법을 알고 싶습니다.

최신 정보

이 질문은 원래 질문을 받았으며 2013 년 4 월에 답변되었습니다. 2014 년 5 월 Gil Birman이 새로운 대답을 제시했는데 정답으로 바꿨습니다. Gil Birman의 대답에 대한 투표가 극히 적기 때문에이 질문을 읽는 사람들은 더 많은 투표를 통해 다른 답변에 찬성하여 답변을 무시하게됩니다. 가장 좋은 답변을 결정하기 전에 Gil Birman의 대답을 적극 권장합니다.




이 질문에는 문맥 적 요소가 있다고 생각합니다.

단순히 서비스에서 데이터를 가져 와서 해당 정보를 복사하는 경우 서비스 속성에 직접 바인딩하는 것이 좋습니다. 필자는 서비스 프로퍼티를 모델 프로퍼티에 맵핑하여 단순히 내 뷰에서 소비 하는 많은 보편적 인 코드 를 작성 하고 싶지 않습니다.

또한 각도의 성능은 두 가지를 기반으로합니다. 첫 번째는 한 페이지에 몇 개의 바인딩이 있는지입니다. 두 번째는 getter 함수가 얼마나 비쌉니 까. Misko가 here 에 대해 이야기합니다 here

서비스 데이터에 인스턴스 특정 논리를 수행해야하는 경우 (서비스 자체에 적용되는 데이터 마사지와 대조적으로)이 결과가 뷰에 노출 된 데이터 모델에 영향을 미치면 $ watcher가 적절하다고 말합니다. 오래 기능은 대단히 비싸지 않다. 비싼 함수의 경우에는 결과를 로컬 (컨트롤러로) 변수에 캐싱하고 $ watcher 함수 외부에서 복잡한 연산을 수행 한 다음 스코프를 결과에 바인딩하는 것이 좋습니다.

주의 사항으로 $ scope에서 직접 속성을 매달아서는 안됩니다. $scope 변수는 여러분의 모델이 아닙니다. 모델에 대한 참조가 있습니다.

내 마음 속에서 단순히 서비스에서 정보를보기 위해 아래로 내려 보내기위한 "모범 사례"

function TimerCtrl1($scope, Timer) {
  $scope.model = {timerData: Timer.data};
};

그리고 당신의 견해는 {{model.timerData.lastupdated}} 포함 할 것입니다.




서비스를 전송하는 모든 데이터를 바인드하는 것은 좋은 생각이 아닙니다 (아키텍처).하지만 더 이상 필요하지 않으면 두 가지 방법으로 제안하십시오.

1) 서비스 안에 있지 않은 데이터를 얻을 수 있습니다. 컨트롤러 / 지시문 내에서 데이터를 가져올 수 있으며 어디서나 바인딩 할 수 있습니다.

2) angularjs 이벤트를 사용할 수 있습니다. 원하는 경우 언제든지 원하는 신호를 보내고 ($ rootScope에서) 신호를 보낼 수 있습니다. 해당 eventName에 대한 데이터를 보낼 수도 있습니다.

아마도 이것은 당신을 도울 수 있습니다. 예제가 더 필요할 경우 여기 링크가 있습니다.

http://www.w3docs.com/snippets/angularjs/bind-value-between-service-and-controller-directive.html




나는 서비스 자체 의 속성 대신에 서비스 자체에 바인딩 하는 것이 더 좋은 방법이라고 생각한다.

이유는 다음과 같습니다.

<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.7/angular.min.js"></script>
<body ng-app="BindToService">

  <div ng-controller="BindToServiceCtrl as ctrl">
    ArrService.arrOne: <span ng-repeat="v in ArrService.arrOne">{{v}}</span>
    <br />
    ArrService.arrTwo: <span ng-repeat="v in ArrService.arrTwo">{{v}}</span>
    <br />
    <br />
    <!-- This is empty since $scope.arrOne never changes -->
    arrOne: <span ng-repeat="v in arrOne">{{v}}</span>
    <br />
    <!-- This is not empty since $scope.arrTwo === ArrService.arrTwo -->
    <!-- Both of them point the memory space modified by the `push` function below -->
    arrTwo: <span ng-repeat="v in arrTwo">{{v}}</span>
  </div>

  <script type="text/javascript">
    var app = angular.module("BindToService", []);

    app.controller("BindToServiceCtrl", function ($scope, ArrService) {
      $scope.ArrService = ArrService;
      $scope.arrOne = ArrService.arrOne;
      $scope.arrTwo = ArrService.arrTwo;
    });

    app.service("ArrService", function ($interval) {
      var that = this,
          i = 0;
      this.arrOne = [];
      that.arrTwo = [];

      $interval(function () {
        // This will change arrOne (the pointer).
        // However, $scope.arrOne is still same as the original arrOne.
        that.arrOne = that.arrOne.concat([i]);

        // This line changes the memory block pointed by arrTwo.
        // And arrTwo (the pointer) itself never changes.
        that.arrTwo.push(i);
        i += 1;
      }, 1000);

    });
  </script>
</body> 

이 plunker 에서 재생할 수 있습니다.




가장 우아한 솔루션 ...

app.service('svc', function(){ this.attr = []; return this; });
app.controller('ctrl', function($scope, svc){
    $scope.attr = svc.attr || [];
    $scope.$watch('attr', function(neo, old){ /* if necessary */ });
});
app.run(function($rootScope, svc){
    $rootScope.svc = svc;
    $rootScope.$watch('svc', function(neo, old){ /* change the world */ });
});

또한 EDA (Event-Driven Architectures)를 작성하므로 다음과 같은 작업을 수행하는 경향이 있습니다.

var Service = function Service($rootScope) {
    var $scope = $rootScope.$new(this);
    $scope.that = [];
    $scope.$watch('that', thatObserver, true);
    function thatObserver(what) {
        $scope.$broadcast('that:changed', what);
    }
};

그런 다음 원하는 채널의 컨트롤러에 리스너를 넣고이 방법으로 내 로컬 범위를 최신 상태로 유지합니다.

결론적으로, 당신이 솔리드 상태로 유지하고 약한 커플 링을 채택하고있는 한, "베스트 프랙티스"는별로 없다. 후자의 코드를 옹호해야하는 이유는 EDA가 본질적으로 가능한 가장 낮은 커플 링을 가지고 있기 때문입니다. 그리고이 사실을 너무 걱정하지 않는다면 같은 프로젝트에 함께 작업하는 것을 피하십시오.

희망이 도움이 ...




Related