简体   繁体   中英

AngularJS learning about services and factories, a service in a factory?

I'm really trying to wrap my head around angular's service/factory/provider constructs before I refactor a pretty big project.

I've read lots of docs and articles on services vs. factories and thought I understood how each of them are created and what they do.

However, while trying stuff I attempted to use a service in a factory or two...

This was really useful: I now understand that there is only one of my 'jsonService' (it's a singleton), so this simple approach WILL NOT WORK... (I'll need each factory to have a separate instance of something)

.service('jsonService', ['$http', function ($http) {
    var data= {'msg':'no data'};
    this.serviceData= data;
    this.get= function(url){
        $http.get(url).then(function (resp) {
            data= resp.data;
        }); 
    }
}])
.factory('fac1', ['jsonService', function(jsonService){
    jsonService.get('json/data1.json');
    return jsonService.serviceData;
}])
.factory('fac2', ['jsonService', function(jsonService){
    jsonService.get('json/data2.json');
    return jsonService;
}])

When I use the factories in a controller like:

myController.f1= fac1;
myController.f2= fac2.serviceData;

I can see that fac1 and fac2 both return the same object, they both have {msg:'no data'} , if I change one then they both change.

My question is:

Even though I can break on the service and see data= {msg:'no data'} and see it being set to the response data - why do I not see any change in fac1 or fac2 ?

All I can think is that somewhere there must be more than one var data , something is not a 'singleton' ????

EDIT: I have now tried:

this.serviceData= function(){return data;};

and:

myController.f2= fac2.serviceData();  // this is always the 'no data' object
myController.f3= fac2.serviceData;

if I then (a long time later) call:

var something= myController.f3();

then I do get the json data... but myController.f2 is still {msg:'no data'} why ?

Ok, after trying meconroy's suggestion I finally figured it out...

The problem is nothing to do with angular, it's how javascript passes objects. (see: Is JavaScript a pass-by-reference or pass-by-value language? )

The factories passed back a 'copy-reference' of the original {msg:'no data'} object, and when the service eventually assigned:

data= resp.data;

that replaced 'data', but the factory-supplied references persist as the old object.

Now, if I do:

.service('jsonService', ['$http', function ($http) {
    var data= {'msg':'no data', 'result':null};
    this.serviceData= data;
    this.get= function(url){
        $http.get(url).then(function (resp) {
            data.result= resp.data;  // update properties of the data object
            data.msg='got data!';    // instead of replacing it
        }); 
    }
}])

...then everything makes sense!

The variables in myController are changed when data arrives (since I have not 'swapped out' the data object).

Obviously, I still have the problem that my two factories return the same object (I'll look at Sacho's suggestion on this) - but I think I've learned something pretty fundamental here.

Try this:

this.serviceData = function(){
    return data;
};

Also you probably have a race situation. $http may not have returned by the time the application asks for the serviceData

The difference between angular's "services" and "factories" is miniscule - I suggest just using one or the other, and sticking with it. From this point out, I'll refer to these elements as "services", even though I exclusively use angular.factory to declare them.

For what you ultimately want to do, there is a much simpler solution - simply return the promise from your service.

this.get= function(url){
    return $http.get(url)
}

Then in your controller/directive/whatever, just use it like:

jsonService.get(url).then(function success(response) {
    // do things with the response
})

But you seem to want to use a service to create many instances, so here's a contrived example achieving that:

.factory('jsonService', jsonService)

jsonService.$inject = ['$http']
function jsonService($http) {
    return Fixed

    function Fixed(url) {
        this.url = url
        this.promise = null
        this.get = get.bind(this)

        function get() {
            // This caches the request so you only do it once, for example
            if (this.promise == null) {
                this.promise = $http.get(url)
            }
            return this.promise
        }
    }
}

Then in your controller, you would do something like:

var fixedOne = new jsonService('fixedOne')
fixedOne.get().then(...)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM