AngularJS service inheritance issues
I have a base service which looks like this:
.service('BaseImageService', ['$q', 'ApiHandler', 'UploadService', function ($q, api, uploadService) {
// Get our api path
var apiPath = 'logos';
// Creates our logo
var _createLogo = function (model) {
// Handle our uploads
return _handleUploads(model).then(function () {
// Create our logo
return api.post(apiPath, model);
});
};
// Edit our logo
var _editLogo = function (model) {
// Handle our uploads
return _handleUploads(model).then(function () {
// Create our logo
return api.put(apiPath, model);
});
};
// Handles our files
var _handleUploads = function (model) {
// Create a promises array
var promises = [];
// Get our file
var file = model.file,
old = model.old;
// If we have a file
if (file) {
// Try to upload the file
promises.push(uploadService.upload(model.file).then(function (response) {
// Update our model
model.path = response.path;
model.thumbnail = response.thumbnail;
}));
// If we have an old model
if (old) {
// Delete both our files
promises.push(uploadService.delete(old.path));
promises.push(uploadService.delete(old.thumbnail));
}
}
// After all promises have completed
return $q.all(promises);
};
// Create our service
var service = {
// Update our api path
updateApiPath: function (path) {
// Set the api path
apiPath = path;
},
// Gets a list of logos
list: function (t) {
if (t) {
console.log(apiPath);
}
// Get our logo
return api.get(apiPath);
},
// Get a single logo
get: function (id) {
// Get our logo
return api.get(apiPath, { id: id });
},
// Create our logo
save: function (model) {
// If we are editing
if (model.id) {
// Edit our logo
return _editLogo(model);
// If we are creating
} else {
// Create our logo
return _createLogo(model);
}
},
// Deletes our logo
delete: function (id) {
// Delete our logo
return api.delete(apiPath, { id: id });
},
// Prepare for editing
prepareForEditing: function (model) {
// Create our old object
model.old = {
path: model.path,
thumbnail: model.thumbnail
};
}
};
// Return our service
return service;
}])
and then I have a few services which "inherit" this service, like this:
.service('ImageService', ['BaseImageService', function (baseService) {
// Get our api path
var apiPath = 'images';
// Update the apiPath
baseService.updateApiPath(apiPath);
// Return our service
return baseService;
}])
.service('LogoService', ['BaseImageService', function (baseService) {
// Get our api path
var apiPath = 'logos';
// Update the apiPath
baseService.updateApiPath(apiPath);
// Return our service
return baseService;
}])
.service('PlayerTextService', ['BaseImageService', function (baseService) {
// Get our api path
var apiPath = 'playerText';
// Update the apiPath
baseService.updateApiPath(apiPath);
// Return our service
return baseService;
}])
I thought that this was working fine. But I have this page that calls all 3 services (ImageService, LogoService and PlayerTextService) sequentially. On the first view of the page everything is fine, if I navigate away and then come back the images service actually pulls back thing from the player text service. Now I know this is because of services being singletons but I am not sure how to fix my issue.
Can anyone give me a hand?
I have added a codepen with an attempted solution:
http://codepen.io/r3plica/pen/ONVBJO
Attempt 2
http://codepen.io/r3plica/pen/jqPeMQ?editors=1010
The solution you tried doesn't work because the BaseService is a singleton. So you inject exactly the same instance into all three service registration functions and all of them configure the same object. So basically the last one wins.
Looks like you want to have separate services with different configurations. This is what providers are used for. They allow a two-step process of building a service instance. Please see this great Stackoverflow answer on the topic:
AngularJS: Service vs provider vs factory
For reference, Restangular is a library that needs to achieve exactly the same as you want. You could use this as a blueprint and see how Restangular handles this requirement:
https://github.com/mgonto/restangular#how-to-create-a-restangular-service-with-a-different-configuration-from-the-global-one
Please be aware that these concepts are based on AngularJS 1 and you need to handle this differently when you want to use AngularJS 2 later on.
After a lot of messing around; I finally found a solution adapting this bit of code
My base service looks like this:
.factory('BaseImageService', ['$q', 'ApiHandler', 'UploadService', 'vectorExtensions', function ($q, api, uploadService, vectorExtensions) {
// Creates our logo
var _createLogo = function (model) {
// Handle our uploads
return _handleUploads(model).then(function () {
// Create our logo
return api.post(BaseImageService.apiPath, model);
});
};
// Edit our logo
var _editLogo = function (model) {
// Handle our uploads
return _handleUploads(model).then(function () {
// Create our logo
return api.put(BaseImageService.apiPath, model);
});
};
// Handles our files
var _handleUploads = function (model) {
// Create a promises array
var promises = [];
// Get our file
var file = model.file,
old = model.old;
// If we have a file
if (file) {
// Try to upload the file
promises.push(uploadService.upload(model.file).then(function (response) {
// Update our model
model.path = response.path;
model.thumbnail = response.thumbnail;
model.fileName = response.fileName;
}));
// If we have an old model
if (old) {
// Delete both our files
promises.push(uploadService.delete(old.path));
promises.push(uploadService.delete(old.thumbnail));
}
}
// After all promises have completed
return $q.all(promises);
};
// Addes a property to the image array to state if they are vector images or not
var _addVectorProperties = function (images) {
// Loop through our images
for (var i = 0; i < images.length; i++) {
// Get our current image
var image = _addVectorProperty(images[i]);
}
// Return our images
return images;
};
// Adds a property to the image to state if it is vector or not
var _addVectorProperty = function (image) {
// Vector flag
var vector = false;
// Get our file extension
var parts = image.path.split('.');
// If we have any parts
if (parts.length) {
// Get our last part
var ext = parts[parts.length - 1],
index = vectorExtensions.indexOf(ext);
// If our extension exists in our vector array
if (index > -1) {
// Change our vector property
vector = true;
}
}
// Update our image with the new property
image.vector = vector;
// Return our image
return image;
};
// Create our service
var BaseImageService = function (path) {
// Set our apiPath
this.apiPath = path;
// Update our api path
this.updateApiPath = function (path) {
// Set the api path
apiPath = path;
};
// Gets a list of logos
this.list = function () {
// Get our logo
return api.get(this.apiPath).then(_addVectorProperties);
};
// Get a single logo
this.get = function (id) {
// Get our logo
return api.get(this.apiPath, { id: id }).then(_addVectorProperty);
};
// Create our logo
this.save = function (model) {
// If we are editing
if (model.id) {
// Edit our logo
return _editLogo(model);
// If we are creating
} else {
// Create our logo
return _createLogo(model);
}
};
// Deletes our logo
this.delete = function (id) {
// Delete our logo
return api.delete(this.apiPath, { id: id });
};
// Set our active image
this.setActive = function (images, image) {
// Loop through our images
for (var i = 0; i < images.length; i++) {
// Get our current image
var current = images[i];
// Set whether we are active or not
current.active = image.id === current.id ? true : false;
}
};
// Prepare for editing
this.prepareForEditing = function (model) {
// Create our old object
model.old = {
path: model.path,
thumbnail: model.thumbnail
};
};
};
// Return our service
return BaseImageService;
}])
and the child services look like this:
.service('ImageService', ['BaseImageService', function (baseService) {
// Create our base service
var child = new baseService('images');
// Return our new service
return child;
}])
.service('LogoService', ['BaseImageService', function (baseService) {
// Create our base service
var child = new baseService('logos');
// Return our new service
return child;
}])
.service('PlayerTextService', ['BaseImageService', function (baseService) {
// Create our base service
var child = new baseService('playerText');
// Return our new service
return child;
}])
That works fine.
Yes it append because it's singleton. You have to perform inheritance if you want to do this.
Here is a code that i use and append to angular :
angular.baseResourceServiceMaker = function(service){
return ['$injector', '$resource', 'TypeService', '$http', '_', 'BackEndpoint',
function($injector, $resource,TypeService, $http, _, BackEndpoint){
//skipping not interesting code
// sample fields to inherits
this.sample = "test";
this.sampleFN = function(){[...]}
// THE line that does the magic
$injector.invoke(service, this);
}
Now time of usage
.service('MyService',angular.baseResourceServiceMaker(['$http', function($http){
// overriding fields
this.sample="inherits";
this.sampleFN = function(){[...]}
}]));
So basically what do we have here ? A function baseResourceServiceMaker which represent a generic particular service. The $injector that call the service we want to instantiate and set the scope to the generic service, so the this on the child class will bind to the same reference than the generic class. The generic service will be instantiated as many times you call it, no confict.
I personally use this code for the resource module of angular to define some basic methods having a custom serializer / deserializer than handle dates and some other stuffs. In your case the baseResourceServiceMaker will be your baseImageService with ending with the $injector.invoke(service, this).
EDIT : found a link with something probably cleaner : AngularJS service inheritance
链接地址: http://www.djcxy.com/p/90468.html上一篇: 用apache commons cli定义位置参数
下一篇: AngularJS服务继承问题