How to reuse functions in an AngularJS factory? - angularjs

I have an AngularJS factory for some common local storage manipulation. It's a common set of functions against different variables. I am constructing it so that the functions are repeated depending on which variable needs to be manipulated. Likely not an elegant way to go about this so open to options.
The factory looks as follows. Is there a way to reuse functions depending on the variable without so much code bloat?
angular.module('app.datastore', [])
.factory('DataStore', function() {
var venue = angular.fromJson(window.localStorage['venue'] || '[]');
var prize = angular.fromJson(window.localStorage['prize'] || '[]');
function persist_venue() {
window.localStorage['venue'] = angular.toJson(venue);
}
return {
list_venue: function () {
return venue;
},
get_venue: function(venueId) {
for (var i=0; i<venue.length; i++) {
if (venue[i].id === venueId) {
return venue[i];
}
}
return undefined;
},
create_venue: function(venueItem) {
venue.push(venueItem);
persist_venue();
},
list_prize: function () {
return prize;
},
get_prize: function(prizeId) {
for (var i=0; i<prize.length; i++) {
if (prize[i].id === prizeId) {
return prize[i];
}
}
return undefined;
},
create_prize: function(prizeItem) {
venue.push(prizeIem);
persist_prize();
}
};
});

My approach is to return in the factory a function which will return a store of a type (venue, prize, ...)
angular.module('app.datastore', [])
.factory('DataStore', function () {
var getStoreFunction = function (storeName) {
var store = angular.fromJson(window.localStorage[storeName] || '[]');
function persist() {
window.localStorage[storeName] = angular.toJson(store);
};
return {
list: function () {
return store;
},
getItem: function (id) {
return store.find(function (elem) {
return elem.id === id;
});
},
createItem: function (item) {
store.push(item);
persist(store);
}
}
};
return { getStore : getStoreFunction };
});
you can create unlimited store by using
var venueStore = DataStore.getStore('venue');
//use of your store
venueStore.createItem({
id : venueStore.list().length + 1,
name : 'myVenue' + venueStore.list().length + 1
});
$scope.venues = venueStore.list();
you can create a factory per type if you want or use it directly in your controller as in this example : https://jsfiddle.net/royto/cgxfmv4q/

i dont know if your familiar with John Papa's angular style guide but you really should take a look it might help you with a lot of design questions.
https://github.com/johnpapa/angular-styleguide
anyway - i would recommend you use this approach -
angular.module('app.datastore', [])
.factory('DataStore', function () {
var venue = angular.fromJson(window.localStorage['venue'] || '[]');
var prize = angular.fromJson(window.localStorage['prize'] || '[]');
return {
list_venue: list_venue,
persist_venue: persist_venue,
get_venue: get_venue,
create_venue: create_venue,
list_prize: list_prize,
get_prize: get_prize,
create_prize: create_prize
};
function persist_venue() {
window.localStorage['venue'] = angular.toJson(venue);
}
function list_venue() {
return venue;
}
function get_venue(venueId) {
for (var i = 0; i < venue.length; i++) {
if (venue[i].id === venueId) {
return venue[i];
}
}
return undefined;
}
function create_venue(venueItem) {
venue.push(venueItem);
persist_venue();
}
function list_prize() {
return prize;
}
function get_prize(prizeId) {
for (var i = 0; i < prize.length; i++) {
if (prize[i].id === prizeId) {
return prize[i];
}
}
return undefined;
}
function create_prize(prizeItem) {
venue.push(prizeIem);
persist_prize();
} });
i like this approach because on the top you can see all the functions available in this factory nice and easy,
and you can also reuse every function you expose outside, inside also, so its very effective and organized,
hope that helped,
good luck.

Related

AngularJS Service as Singleton to Storage files

I have I'm trying to write an AngularJS service which should work as singleton in storage files. There should be two methods:
for writing files by key getFilesForTabId
for getting saved files from setFilesForTabId
I'm trying to write something like this:
app.factory('fileStorage', ['LogService', function (LogService) {
var fileStorage = {
this.files = {};
this.getFilesForTabId = function (key) {
return this.files[key];
};
this.setFilesForTabId = function (key, files) {
this.files[key] = files;
return true;
}
}
return fileStorage;
}]);
But this code is bad. There are errors when I'm trying using it. How could I write it? I'd grateful for help
Now I have a problem with getFilesForTabId function. I'm trying to run this function with undefined files[key] object.
My actual service code is:
app.factory('fileStorage', ['LogService', function (LogService) {
var fileStorage = {
files: {},
setFilesForTabId: function(key,files){
this.files[key] = files;
return true;
},
getFilesForTabId: function (key) {
if(typeof(files[key]) === undefined) {
return [];
}
else{
return this.files[key];
}
}
}
return fileStorage;
}]);
Below I show error from browswer:
You can't use = in {} object.
var fileStorage =
{
files: {},
getFilesForTabId: function (key) {
return this.files[key];
},
setFilesForTabId: function (key, files) {
this.files[key] = files;
return true;
}
};
you are trying to initialize fileStorage as an object but are writing it like a function instead. you need to use Object Initializer Syntax.
Try this instead:
app.factory('fileStorage', ['LogService', function(LogService) {
var fileStorage = {
files: {},
getFilesForTabId: function(key) {
return this.files[key];
},
setFilesForTabId: function(key, files) {
this.files[key] = files;
return true;
},
};
return fileStorage;
}]);

Why array save last data and doesn't clears?

I have a simple AngularJs application of medical cards.
I have storage with it and display it at my home.html using dx-datagrid:
One card has many records, I get records of card from recordsArray by cardId
getVardsRecordsByCardId: function (id, recordsArray) {
if (recordsArray.length != 0) {
for (var i = 0; i < recordsArray.length; i++) {
if (recordsArray[i].cardId === id) {
cardsRecords = cardsRecords.concat(recordsArray[i]);
}
}
}
return cardsRecords;
}
Now I have records just in the third card. I added a function on button for testing it:
var jdskla = [];
var localCardId = 0;
$scope.showCardDetails = {
text: "",
type: "default",
icon: "preferences",
onClick: function () {
if ($scope.itemIdFromShowButton) {
$location.path('/carddetail/' + $scope.itemIdFromShowButton);
var jdskla =[];
var jdskla = businessLogicOfMyApp.getVardsRecordsByCardId($scope.itemIdFromShowButton, $scope.recordsArray);
console.log($scope.itemIdFromShowButton)
console.log(jdskla);
}
else {
alert("Error!!!");
}
}
};
1,3,1 is cardId's and array of records. But, why array of card records don't clears and save last data?
May be somebody know how I can resolve it? Thanks for your answers!
P.S. I'm using ng-view directive in my app and i tried to clear my array use another button:
$scope.backToGeneralPage = {
text: "Back",
onClick: function () {
jdskla = [];
$location.path('/');
}
};
but it wasn't helpful.
You should initialize cardsRecords array in function getVardsRecordsByCardId.
getVardsRecordsByCardId: function (id, recordsArray) {
var cardsRecords = []; // initialize array locally
if (recordsArray.length != 0) {
for (var i = 0; i < recordsArray.length; i++) {
if (recordsArray[i].cardId === id) {
cardsRecords.push(recordsArray[i]);
}
}
}
return cardsRecords;
}

Simplify Angularjs conditionals inside a loop

Is there a better way of doing the following conditionals in a loop in an angularjs controller?
angular.forEach(vm.brgUniversalDataRecords, function (value) {
if (value.groupValue2 == 1) {
vm.graphSwitch1 = value.groupValue3;
};
if (value.groupValue2 == 2) {
vm.graphSwitch2 = value.groupValue3;
};
if (value.groupValue2 == 3) {
vm.graphSwitch3 = value.groupValue3;
};
});
Is there a simplified version?
Thanks.
You can create an object that contains a key value pair with your actions.
var Actions = {
1 : function () { vm.graphSwitch1 = value.groupValue3; },
2 : function () { vm.graphSwitch2 = value.groupValue3; },
3 : function () { vm.graphSwitch3 = value.groupValue3; }
};
var action = value.groupValue2;
if (Actions.hasOwnProperty(action)) {
Actions[action]();
}

How to call function recursively

I'm trying to call my mapper function recursively but getting the error not defined: 'ReferenceError: mapper is not defined at Object.mapper'
Could use some guidance on how to call the function recursively in this particular situation.
angular.module('dvb.transferObjects').value('MappedTransferObject', function(obj1) {
'use strict';
return {
mapper: function(obj2) {
for (var p in obj1) {
if (typeof obj1[p] === 'object') {
mapper(obj1[p], obj2[p]);
} else {
if(obj2.hasOwnProperty(p)) {
obj1[p] = obj2[p];
}
}
}
return obj1;
}
};
});
I'm injecting this value in my controller as MTO and using it as follows:
var mto = new MTO(appState.getTemplateObject());
var mappedObject = mto.mapper($scope.dvModel);
If you add a name to your anonymous function you can call it inside itself like so:
angular.module('dvb.transferObjects').value('MappedTransferObject', function (obj1) {
'use strict';
return {
mapper: function mapper(obj2) {
for (var p in obj1) {
if (typeof obj1[p] === 'object') {
mapper(obj1[p], obj2[p]);
} else {
if (obj2.hasOwnProperty(p)) {
obj1[p] = obj2[p];
}
}
}
return obj1;
}
};
});
The reason you can not use mapper in your example is due to the mapper being scoped to the object itself. This means the only way to access the function would be to call it through the object, which you can't do without saving off a reference to the object before returning it:
angular.module('dvb.transferObjects').value('MappedTransferObject', function (obj1) {
'use strict';
var mapper = {
mapper: function (obj2) {
for (var p in obj1) {
if (typeof obj1[p] === 'object') {
mapper.mapper(obj1[p], obj2[p]);
} else {
if (obj2.hasOwnProperty(p)) {
obj1[p] = obj2[p];
}
}
}
return obj1;
}
};
return mapper;
});

Getting the response from an angular function

I have the following scope function in my controller
$scope.getStaff = function (request, response) {
var a = [];
if ($scope.staff !== null) {
// all terms must be contained
a = $scope.staff;
var terms = request.term.toLowerCase().split(' ');
for (var i = 0; i < terms.length; i++) {
var t = terms[i];
if (t) {
a = $.grep(a, function (item, index) {
var v = item.label.toLowerCase();
return v.indexOf(t) !== -1;
});
}
}
}
response(a.length > 0 ? a : null);
};
I'm attempting to test it using jasmine like this:
describe('getStaff', function() {
it('...', function() {
$scope.staff = [
{ label: "test1" },
{ label: "test2" }
];
var req = { term: "a b c" };
expect(req.term.toLowerCase()).toBe('a b c');
var res = function(a) {
return a;
}
var result = $scope.getStaff(req, res).response;
expect(result).toBe(null);
});
});
I'm ultimately trying to see what "a" is in the getStaff function. How can I get that value in my jasmine test?
My answer is really an opinion. Unless you are willing to expose your 'a' in the scope of the controller, then my answer would be "you don't care" your jasmine test should only be testing the answer of response(a.length > 0 ? a : null); returned.
My gut tells me you may want to consider creating a helper function for this code
$.grep(a, function (item, index) {
var v = item.label.toLowerCase();
return v.indexOf(t) !== -1;
});
and unit testing that separate from your getStaff function.

Resources