AngularJS : The correct way of binding to a service properties

前端 未结 10 1824
天命终不由人
天命终不由人 2020-11-28 00:30

I’m looking for the best practice of how to bind to a service property in AngularJS.

I have worked through multiple examples to understand how to bind to properties

相关标签:
10条回答
  • 2020-11-28 01:00

    From my perspective, $watch would be the best practice way.

    You can actually simplify your example a bit:

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

    That's all you need.

    Since the properties are updated simultaneously, you only need one watch. Also, since they come from a single, rather small object, I changed it to just watch the Timer.data property. The last parameter passed to $watch tells it to check for deep equality rather than just ensuring that the reference is the same.


    To provide a little context, the reason I would prefer this method to placing the service value directly on the scope is to ensure proper separation of concerns. Your view shouldn't need to know anything about your services in order to operate. The job of the controller is to glue everything together; its job is to get the data from your services and process them in whatever way necessary and then to provide your view with whatever specifics it needs. But I don't think its job is to just pass the service right along to the view. Otherwise, what's the controller even doing there? The AngularJS developers followed the same reasoning when they chose not to include any "logic" in the templates (e.g. if statements).

    To be fair, there are probably multiple perspectives here and I look forward to other answers.

    0 讨论(0)
  • 2020-11-28 01:02

    I think this question has a contextual component.

    If you're simply pulling data from a service & radiating that information to it's view, I think binding directly to the service property is just fine. I don't want to write a lot of boilerplate code to simply map service properties to model properties to consume in my view.

    Further, performance in angular is based on two things. The first is how many bindings are on a page. The second is how expensive getter functions are. Misko talks about this here

    If you need to perform instance specific logic on the service data (as opposed to data massaging applied within the service itself), and the outcome of this impacts the data model exposed to the view, then I would say a $watcher is appropriate, as long as the function isn't terribly expensive. In the case of an expensive function, I would suggest caching the results in a local (to controller) variable, performing your complex operations outside of the $watcher function, and then binding your scope to the result of that.

    As a caveat, you shouldn't be hanging any properties directly off your $scope. The $scope variable is NOT your model. It has references to your model.

    In my mind, "best practice" for simply radiating information from service down to view:

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

    And then your view would contain {{model.timerData.lastupdated}}.

    0 讨论(0)
  • 2020-11-28 01:06

    What about

    scope = _.extend(scope, ParentScope);
    

    Where ParentScope is an injected service?

    0 讨论(0)
  • 2020-11-28 01:06

    The Most Elegant Solutions...

    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 */ });
    });
    

    Also, I write EDAs (Event-Driven Architectures) so I tend to do something like the following [oversimplified version]:

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

    Then, I put a listener in my controller on the desired channel and just keep my local scope up to date this way.

    In conclusion, there's not much of a "Best Practice" -- rather, its mostly preference -- as long as you're keeping things SOLID and employing weak coupling. The reason I would advocate the latter code is because EDAs have the lowest coupling feasible by nature. And if you aren't too concerned about this fact, let us avoid working on the same project together.

    Hope this helps...

    0 讨论(0)
提交回复
热议问题