AngularJS, is this way of using service good?

20,498

Solution 1

AngularJS templates can only invoke functions that are available on a scope. So, whatever approach you take you need to have your function on a scope.

If you want your service's functions to be directly invokable from a template you've got several options:

The one you've tried - that is, expose the whole service on a scope:

$scope.service = service;

and then in a template:

<p>Hello {{service.getUsername();}}</p>

This is one-liner in a controller and makes all the service methods available on a scope and thus to templates.

Expose methods one by one

to have precise control over what gets exposed:

$scope.getUsername = service.getUsername;

and then in a template:

<p>Hello {{getUsername();}}</p>

This requires more work exposing methods but gives you fine-grained control over what gets exposed.

Expose proxing methods:

$scope.getMyUsername = function() {
   //pre/post processing if needed 
   return service.getUsername();
};

You can use any of those methods or mix and combine them but at the end of the day a function must end up on a scope (either directly or through another object exposed on a scope).

Solution 2

Another way to do it:

Expose the service on $rootScope:

$rootScope.service = service;

and then in a template:

<p>Hello {{service.getUsername();}}</p>

You can do this on app.run, and you will get the service in all the views of your app. You could use this method for Authentication services.

Solution 3

Another way to expose your service within your $scope would be to add a function pointer to your service method/data object.

scope.serviceData = service.data;
// Or
scope.getServiceData = service.getData;

Within your view you can then invoke it by using parentheses.

<input ng-model="serviceData().key" />
// Or
<input ng-model="getServiceData().key" />
// Or
{{getServiceData().key}}

I personally like this approach and i am currently using it in order to keep multiple views in sync with the same data. It does bring up some issues though as explained here: AngularJS. Best practice concerning proper two way data binding from a service

As to exposing to much data i am currently trying to do something like this.

// Within your view.
{{getServiceDataByKey('key')}}

// In your controller.
scope.getServiceDataByKey = service.getServiceDataByKey;

// In your service.
getServiceDataByKey : function (key) {
   return dataObject[key];
}

The reason why i am doing this is that we want to keep the controllers as clean as possible and have all our data in one centralized place. Also most data within the service should be exposed.

Share:
20,498
Bruno
Author by

Bruno

I'm Bruno Scopelliti, web developer from Bologna, Italy. @brunoscopelliti

Updated on August 17, 2020

Comments

  • Bruno
    Bruno almost 4 years

    i've this HTML:

    <p>Hello {{name}}</p>
    

    and the controller is:

    function myCtrl(scope, service) {
        scope.name = service.getUsername(); // service.getUsername() return "World!"
    }
    myCtrl.$inject = ['$scope', 'originalService'];
    

    The service works fine, so i don't paste the code here... In this case the result is "Hello world!" I changed the HTML in this way:

    <p>Hello {{service.getUsername()}}</p>
    

    But this does not work.

    I changed the controller:

    function myCtrl(scope, service) {
        scope.ser = service;
    }
    myCtrl.$inject = ['$scope', 'originalService'];
    

    and then the HTML

    <p>Hello {{ser.getUsername();}}</p>
    

    This works!

    So my question is:

    Is this the only way to use the functions of a service directly in the HTML, or i'm missing something?