Pregunta

I have seen 2 ways to do this. See the service/controllers:

var app = angular.module('myApp', []);

app.service('user', function() {    
    this.loggedIn = false;
    this.logIn = function() { 
        this.loggedIn = true; 
    };
    this.logOut = function() {
        this.loggedIn = false;
    };
});

function Ctrl1($scope, user) {
    $scope.user = user;
}

function Ctrl2($scope, user) {
    $scope.loggedIn = user.loggedIn;

    // Proxy functions

    // This wont work because of binding:
    // $scope.logIn = user.logIn;
    // $scope.logOut = user.logOut;

    // This works
    $scope.logIn = function() {
        user.logIn();
    };
    $scope.logOut = function() {
        user.logOut(); 
    };

    // This also works:
    // $scope.logIn = user.logIn.bind(user);
    // $scope.logOut = user.logOut.bind(user);

    $scope.$watch(function() {
        return user.loggedIn;
    }, function(value) {
        $scope.loggedIn = value;
    });
}

And HTML:

<div ng-controller="Ctrl1">
    <h2>Ctrl1: Access the user service directly</h2>
    <div ng-show="user.loggedIn">
        Logged In
        <button ng-click="user.logOut()">Log Out</button>
    </div>
    <button ng-show="!user.loggedIn" ng-click="user.logIn()">Log In</button>    
</div>
<hr />
<div ng-controller="Ctrl2">
    <h2>Ctrl2: Proxy via the controller</h2>
    <div ng-show="loggedIn">
        Logged In
        <button ng-click="logOut()">Log Out</button>
    </div>
    <button ng-show="!loggedIn" ng-click="logIn()">Log In</button>  
</div>

Live: http://jsfiddle.net/h6AR4/

Adding the service directly to the scope feels wrong but results in much cleaner controller code.

Proxying all the functionality results in messy controller code.

Is there a definitive 'Angular way'?

¿Fue útil?

Solución

The method you've outlined above is "supported" in the sense that you've bound the entire service to $scope. IMHO, it's usually not best (I'd label it a code smell, if you will) to let your template "peek" into all the methods that are defined in the service.

In addition, calling an AngularJS service (without proxying methods) now makes a more coupled and harder to test implementation of your application. That said, it may make sense to follow your leaner approach if:

  • Your service (and controller) are light-weight and pass-through where the additional overhead (of writing proxy methods) is not worth it.
  • Testing (and more importantly, mocking) aren't very important to your use case or the code is easy to verify through other methods
  • Your service returns data instantly (rather than a deferred/Promise value). An deferred value would need a controller method to both handle the "loading" state as well as unwrap the Promise result (when its available).

Overall, YMMV here but there's not a definitive reason why the direct binding isn't "correct" or "allowed".

Pawel has a good writeup that presents the same options but the consensus amongst the community is also that while it's an option, it's rarely the "right" option with this method of binding: https://stackoverflow.com/a/15040125/283788

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top