Creating angular base service and sub services - angularjs

I'm trying to create a general service for dynamic listing objects i angular and for different types of Objects I need slightly different methods for this service. So I thought it would be the best to have a base service and some sub-services. The problem is, that I need to initialize the base service with different Objects depending on sub-service.
So that what I got so far:
Base List-Service (shortened to the relevant)
App.factory('List', ['$q',
function (){
var List = function(Item, searchParams){
this.Item = Item;
this.searchParams = searchParams;
//....
this.nextPage();
};
//.....
List.prototype.nextPage = function () {
//.....
this.Item.find({
//.....
}.bind(this));
};
return List;
}]);
Sub-service of List-Service
App.factory('UserList', [
'User', 'List','$q',
function (User, List) {
UserList = function(){
var searchParams = {
// params Object
};
return new List(User, searchParams);
};
// extend base class:
UserList.prototype.updateUser = function(id){
//.....
}
//....
return UserList;
}]);
Currently just the UserList is loaded, but: Of course it loads every time a new instance, due the new operator when it's called, but I just want one instance. But leaving the new operator throw's an error that this.nextPage(); would be undefined function. Beside this it seems the extension function updateUser is not applied.
So what's the best practice to inherit from other service with passing arguments to parent service in angular?

I gotta work it.
changed sub service to this to inherit proper from base:
App.factory('UserList', [
'User', 'List','$q',
function (User, List) {
var UserList = function(){
var searchParams = {
//.....
};
List.call(this, User, searchParams);
};
// inherit from List service
UserList.prototype = Object.create(List.prototype);
UserList.prototype.updateUser = function(id) {
//.....
};
return UserList;
}
])
;

Related

AngularJS - Initialize form data based on value

I am trying to add edit functionality to my app. In one view, I have a button that brings you to the edit page.
<button ng-click="editMission(selectedMission.key)">Edit Mission</button>
The value selectedMission.key is used to determine what to initialize the edit page's form data with.
In the controller the function looks like this:
$scope.editMission = function(key){
$location.path('/edit');
}
On the edit page I have:
<div data-ng-init="editInit()">
And in my controller I have:
$scope.editInit = function(){
var query = myDataRef.orderByKey();
query.on("child_added", function(missionSnapshot){
if (missionSnapshot.key()==key){
...
}
});
}
How can I run the initialize function based on the key value from editMission. Should I use some getter/setter approach with a global key variable? I tried just placing the editInit code in editMission but the form data does not populate on view load.
Common practice is to use a service to share variables between views/controllers.
So in your case you would use the getter/setter approach as you suspected. I don't know what exactly you're trying to do, but the service in your case would look something like this:
app.factory('missionKeyService', function() {
var currentMission= {};
return {
setMissionKey: function(missionKey) {
currentMission.key = missionKey;
},
getMissionKey: function() {
return currentMission.key;
}
}
})
And in your controller1:
//include 'missionKeyService' in your controller function params
$scope.editMission = function(key) {
missionKeyService.setMissionKey(key);
$location.path('/edit');
}
And controller2:
//include 'missionKeyService' in your controller function params
$scope.editInit = function() {
var currentKey = missionKeyService.getMissionKey();
//do something with this key
...
}

How do you resolve circular dependencies in Angular

I have a NewsService and PersonService and a model for Person and for NewsItem. The following is a simplified representation excluding services.
//angular service to expose Person
var Person = function(data) {
angular.copy(data, this);
//some other initialisation
}
Person.prototype.getNews = function() {
//fetch news
.then(function(newsItems) {
return newsItems.map(function(newsItemData){
newsItemData.author = new Person(newsItemData.author);
return new NewsItem(newsItemData);
});
});
}
//Angular service to expose NewsItem
var NewsItem = function(data) {
angular.copy(data, this);
//some other initialisation
}
NewsItem.prototype.getComments = function() {
//fetch comments
.then(function(commentsData){
return comments.map(function(commentData) {
commentData.author = new Person(commentData.author);
return new Comment(commentData);
});
});
}
//Angular services that reference these objects
NewsService
PersonService
NewsItem and Person reference each other because the data fetched from their respective APIs includes data that should be instantiated by the other object. There are numerous pairs of services and models referencing each other and I have a problem with circular dependencies forcing me to instantiate objects elsewhere, which I want to avoid. I can't think of an elegant solution and could really use your help.

AngularJS Generic List Controller

So I didn't get an answer from my last question so I decided to handle this myself.
I created a generic controller like this:
.controller('GenericListController', function () {
// Define this
var self = this;
// Define our list
self.list = [];
// Create our page sizes array
self.pageSizes = [10, 20, 50, 100];
// For filtering and sorting the table
self.pageSize = self.pageSizes[0];
self.predicate = 'name';
self.reverse = false;
self.filter = '';
// For deleting
self.delete = function (e, model) {
// Delete the item
service.delete(model.id);
};
});
very simple as you can see.
Now I was using this by injecting it into my controller like this:
.controller('DashboardController', ['GenericListController', 'CenterService', 'companyId', 'centers', function (Controller, service, companyId, centers) {
// Assign this to a variable
var self = Controller;
}])
In theory everything that is assigned to the GenericListController is now available to the DashboardController. The problem is the line in the generic controller that looks like this:
service.delete(model.id);
Somehow I need to reference my service in the generic controller. I thought that maybe I could create a provider and inject the service reference into the constructor but I am not sure if it being a singleton is an issue, so I need some help.
Is a service / factory / provider a good way to build the GenericListController?
Does a service / factory being a singleton affect anything? If so, can they be created so they are not singletons?
Is there another way to achieve what I am after?
Update 1
So it appears some people are confused....
So if I created a factory that looks like this:
.factory('ListControllerService', function () {
// Default constructor expecting a service
return function (service) {
// Define this
var self = this;
// Define our list
self.list = [];
// Create our page sizes array
self.pageSizes = [10, 20, 50, 100];
// For filtering and sorting the table
self.pageSize = self.pageSizes[0];
self.predicate = 'name';
self.reverse = false;
self.filter = '';
// For deleting
self.delete = function (e, model) {
// Delete the item
service.delete(model.id);
};
};
})
then I create 2 separate controllers that looks like this:
.controller('DashboardController', ['ControllerService', 'CenterService', 'companyId', 'centers', function (Controller, service, companyId, centers) {
// Assign this to a variable
var self = new Controller(service);
self.list = centers;
}])
.controller('CompanyController', ['ControllerService', 'CompanyService', 'ArrayService', 'companies', function (Controller, service, arrayService, centers) {
// Assign this to a variable
var self = new Controller(service);
self.list = companies;
}])
Hopefully you can see that the service I am injecting into the ListControllerService is different for each controller. The only caveat I have with my example is that each "service" must have a delete method (not so difficult because they are all api services).
I hope that explains things better.
The solution I am using on my current project is to write a function that registers a factory.
function CreateMyGenericFactory(factoryName, serviceName)
app.factory(factoryName, [serviceName, function (service){
var GenericListFactory = {
list: [],
pageSizes: [10, 20, 50, 100],
predicate: 'name',
reverse: false,
filter: ''
delete: function(e, model){
service.delete(model.id);
}
}
GenericListFactory.pageSize = GenericListFactory.pageSizes[0];
return GenericListFactory;
}]);
}
Then execute the function in your JS to register a new factory dynamically.
CreateMyGenericFactory('ListFactory', 'ListService');
And use it in your controller.
app.controller('GenericListController', ['ListFactory', function (ListFactory) {
...
console.log(ListFactory.pageSizes.length); // -> 4
ListFactory.delete(e, model);
}];
I also registered the service inside my CreateMyGenericFactory function to further reduce duplication, you may consider doing that as well if each factory has its own service.
The final solution looked like this.
function CreateMyGenericFactory(name)
var factoryName = name + 'Factory'; // e.g. listFactory
var serviceName = name + 'Service'; // e.g. listService
app.factory(factoryName, [serviceName, function (service){
var factory = {
list: [],
pageSizes: [10, 20, 50, 100],
predicate: 'name',
reverse: false,
filter: ''
delete: function(e, model){
service.delete(model.id);
}
}
factory.pageSize = factory.pageSizes[0];
return factory;
}]);
app.service(serviceName, ['$resource', function (resource){
return $resource(Modus[backend] + '/api/'+slug+'s/:id', {
id: '#_id'
});
}]);
}
That way I could register all the Factories and Services I needed without duplicating any code.
CreateMyGenericFactory('user');
// this registered userFactory and userService
Well, it looks like I was nearly there.
I have managed to solve this but I am not sure if it is the best way to do it, but it certainly works.
So my service is very similar to before:
.factory('ListControllerService', function () {
// Default constructor expecting a service
return function (ctrl, service) {
// Define this
var self = ctrl;
// Define our list
self.list = [];
// Create our page sizes array
self.pageSizes = [10, 20, 50, 100];
// For filtering and sorting the table
self.pageSize = self.pageSizes[0];
self.predicate = 'name';
self.reverse = false;
self.filter = '';
// For deleting
self.delete = function (e, model) {
// Delete the item
service.delete(model.id);
};
return self;
};
})
The only thing that changes is that instead of:
self = this;
I now do:
self = ctrl;
ctrl is the controller that is inheriting from this service.
I also return self so that I can bind it to self in the inherited controller.
now my controller looks like this:
.controller('DashboardController', ['ListControllerService', 'CenterService', 'companyId', 'centers', function (Controller, service, companyId, centers) {
// Assign this to a variable
var self = new Controller(this, service);
console.log(this);
console.log(self);
// Assign our centers
self.list = centers;
}])
both console.log output the same which is great.

Using AngularJS Service to Check if User has Admin Permissions

I am building an SharePoint App using AngularJS and am attempting to define a service that retrieves if the user is an Admin or not. The service itself is successfully logging/working as expected, but I am not sure how to use this in a controller. My end goal is that when a page loads that is tied to a controller, that this service checks if they are an admin or not. From that point, I can do all sorts of magic (ex. redirect, etc.). Here is my service:
// Check if user is an admin
appServices.factory('appAdminCheck', ['$resource', 'appCurrentUserProfile', 'appAdmins', function ($resource, appCurrentUserProfile, appAdmins) {
var userAdmin = [];
appCurrentUserProfile.query(function (usercheck) {
var userID = usercheck.Id;
appAdmins.query(function (admins) {
var admins = admins.value; // Data is within an object of "value", so this pushes the server side array into the $scope array
// Foreach type, push values into types array
angular.forEach(admins, function (adminvalue, adminkey) {
if (adminvalue.Admin_x0020_NameId == userID) {
userAdmin = true;
console.log("I'm an Admin" + userAdmin);
}
});
});
});
return userAdmin;
}]);
Update: Upon closer inspection, I would like to return the array of values, but it keeps stating that the array length is 0. I am sure it is because I am not "returning" properly.
Here is my updated service:
appServices.factory('appAdminCheck', ['$resource', 'appCurrentUserProfile', 'appAdmins', function ($resource, appCurrentUserProfile, appAdmins) {
var userAdmin = [];
var checkUser = function() {
appCurrentUserProfile.query(function (usercheck) {
var userID = usercheck.Id;
appAdmins.query(function (admins) {
var admins = admins.value; // Data is within an object of "value", so this pushes the server side array into the $scope array
// Foreach type, push values into types array
angular.forEach(admins, function (adminvalue, adminkey) {
if (adminvalue.Admin_x0020_NameId == userID) {
userAdmin.push({
isAdmin: 'Yes',
role: adminvalue.Role,
});
}
});
});
});
return userAdmin;
}
return {
checkUser: checkUser
};
}]);
Here is a logging call in a controller:
var test = appAdminCheck.checkUser();
console.log(test);
Seeing as there appears to be some asynchronous actions happening, you'll want to return a promise. You can do this by chaining the then promise resolution callbacks from your other services (assuming they're $resource instances or similar). For example...
appServices.factory('appAdminCheck', function (appCurrentUserProfile, appAdmins) {
return function() {
return appCurrentUserProfile.query().$promise.then(function(usercheck) {
return appAdmins.query().$promise.then(function(admins) {
// this needs to change if admins.value is not an array
for (var i = 0, l = admins.value.length; i < l; i++) {
if (admins.value[i].Admin_x0020_NameId === usercheck.Id) {
return true;
}
}
return false;
});
});
};
});
Then, you can use this promise resolution in your controller, eg
appAdminCheck().then(function(isAdmin) {
// isAdmin is true or false
});

AngularJS model best practice

I have been looking at this document:
understanding-service-types
Because I am new to AngularJS I am having some problems understanding everything in there. I still don't understand the difference between a factory and a service, but I will leave that for another day.
The problem I have now, is that I created a model as a factory and now I think I may have done it wrong.
Here is my model:
commonModule.factory('optionsModel', function () {
var _options = angular.fromJson(sessionStorage.siteOptions);
var _defaults = {
rotateBackground: false,
enableMetro: true
};
if (_options) {
_defaults.rotateBackground = _options.rotateBackground;
_defaults.enableMetro = _options.enableMetro;
}
var _save = function (options) {
console.log(options);
sessionStorage.siteOptions = angular.toJson(options);
}
return {
options: _defaults,
save: _save
};
});
As you can see here, what I am doing is setting the defaults and then I check to see if we have anything in our session, if we do I then overwrite our options with the new settings.
I also have a save function which is used to save the options to the session.
Is this the best way to make this model or should I be doing it another way?
I don't think you should think about a model in the way you're doing it.
For your purpose, you can do it in a more "angular" way :
commonModule.factory('optionsModel', function () {
var factory = {
getOptions: getOptions,
saveOptions: saveOptions
}
// If you need default values, you can assign those here,
// but you can also think about adding a dependency into your factory,
// that would be bound to your default settings.
return factory;
function getOptions(){
return angular.fromJson(sessionStorage.siteOptions);
}
function saveOptions(options){
sessionStorage.siteOptions = angular.toJson(options)
}
});

Resources