AngularJS Service as Singleton to Storage files - angularjs

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;
}]);

Related

Why my array is still empty even though I had assign value to this array in my controller? (I am using angularjs, not angular)

This question took me one day to debug it, but still no luck.
Problem: this.gridOptions.data = this.allTemplatesFromClassificationRepo ;
**this.allTemplatesFromClassificationRepo ** is still a empty array. Before this line of code executes, I have call activate() function to assign array to **this.allTemplatesFromClassificationRepo **. Please see this line this.allTemplatesFromClassificationRepo = templateReorderProperties;
P.S. Although I cannot get the value of allTemplatesFromClassificationRepo in controller, but I can get the value of it in html.
namespace app.admin {
'use strict';
class FooController {
static $inject: Array<string> = [];
constructor() {
this.activate();
}
activate() {
this.getData();
this.classificationFoo = this.data[0];
this.getTemplateFromGivenRepo(this.classificationFoo.id, this.classificationFoo.displayName);
this.populateData();
}
data: any;
classificationFoo: any;
allDataFromclassificationFoo: any = [];
// demo grid
gridOptions = {
enableFiltering: true,
},
data: []
};
populateData() {
this.gridOptions.data = this.allTemplatesFromClassificationRepo ;
}
getData() {
this.fooService.getUserData();
this.data = this.fooService.userdata;
}
getTemplateFromGivenRepo(fooId: string, fooName: string) {
switch (fooId) {
case 'FOO':
this.TemplateApi.templatesAvaiableForRepoIdGET(fooId).then(data => {
data.forEach(element => {
element.fooName = fooName;
});
let templateReorderProperties = data
this.allTemplatesFromClassificationRepo = templateReorderProperties;
}, error => {
});
break;
default:
break;
}
};
}
class Bar implements ng.IDirective {
static $inject: Array<string> = [];
constructor() {
}
bindToController: boolean = true;
controller = FooController;
controllerAs: string = 'vm';
templateUrl: string = 'app/foo.html';
static instance(): ng.IDirective {
return new Bar();
}
}
angular
.module('app.admin')
.directive('bar', Bar.instance);
}
getTemplateFromGivenRepo is async operation.
Move this.populateGridData(); call inside getTemplateFromGivenRepo after
this.allTemplatesFromClassificationRepo = templateReorderProperties;
getTemplateFromGivenRepo(repoId: string, repoName: string) {
switch (repoId) {
case 'CLASSIFICATION':
this.TemplateApi.templatesAvaiableForRepoIdGET(repoId).then(data => {
this.allTemplatesFromClassificationRepo = templateReorderProperties;
this.populateGridData(); // call here
}, error => {
});
}
};
OR
You can return promise from getTemplateFromGivenRepo and in then able success callback,call
this.populateGridData();
I think the problem is in this instance that different inside Promise resolve.
Try to write in beginning something like:
var self = this;
and after that change all this to self key.
a.e.:
self.gridOptions.data = self.allTemplatesFromClassificationRepo;
// and so on ...
By this way you will guarantee that you use same scope instance
Hope it will work

How to reuse functions in an AngularJS factory?

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.

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;
});

Nodejs async data duplication

I'm having some problems with one async process on nodejs.
I'm getting some data from a remote JSON and adding it in my array, this JSON have some duplicated values, and I need check if it already exists on my array before add it to avoid data duplication.
My problem is when I start the loop between the JSON values, the loop call the next value before the latest one be process be finished, so, my array is filled with duplicated data instead of maintain only one item per type.
Look my current code:
BookRegistration.prototype.process_new_books_list = function(data, callback) {
var i = 0,
self = this;
_.each(data, function(book) {
i++;
console.log('\n\n ------------------------------------------------------------ \n\n');
console.log('BOOK: ' + book.volumeInfo.title);
self.process_author(book, function() { console.log('in author'); });
console.log('\n\n ------------------------------------------------------------');
if(i == data.length) callback();
})
}
BookRegistration.prototype.process_author = function(book, callback) {
if(book.volumeInfo.authors) {
var author = { name: book.volumeInfo.authors[0].toLowerCase() };
if(!this.in_array(this.authors, author)) {
this.authors.push(author);
callback();
}
}
}
BookRegistration.prototype.in_array = function(list, obj) {
for(i in list) { if(list[i] === obj) return true; }
return false;
}
The result is:
[{name: author1 }, {name: author2}, {name: author1}]
And I need:
[{name: author1 }, {name: author2}]
UPDATED:
The solution suggested by #Zub works fine with arrays, but not with sequelize and mysql database.
When I try to save my authors list on the database, the data is duplicated, because the system started to save another array element before finish to save the last one.
What is the correct pattern on this case?
My code using database is:
BookRegistration.prototype.process_author = function(book, callback) {
if(book.volumeInfo.authors) {
var author = { name: book.volumeInfo.authors[0].toLowerCase() };
var self = this;
models.Author.count({ where: { name: book.volumeInfo.authors[0].toLowerCase() }}).success(function(count) {
if(count < 1) {
models.Author.create(author).success(function(author) {
console.log('SALVANDO AUTHOR');
self.process_publisher({ book:book, author:author }, callback);
});
} else {
models.Author.find({where: { name: book.volumeInfo.authors[0].toLowerCase() }}).success(function(author) {
console.log('FIND AUTHOR');
self.process_publisher({ book:book, author:author }, callback);
});
}
});
// if(!this.in_array(this.authors, 'name', author)) {
// this.authors.push(author);
// console.log('AQUI NO AUTHOR');
// this.process_publisher(book, callback);
// }
}
}
How can I avoid data duplication in an async process?
This is because you are comparing different objects and result is always false.
Just for experiment type in the console:
var obj1 = {a:1};
var obj2 = {a:1};
obj1 == obj2; //false
When comparing objects (as well as arrays) it only results true when obj1 links to obj2:
var obj1 = {a:1};
var obj2 = obj1;
obj1 == obj2; //true
Since you create new author objects in each process_author call you always get false when comparing.
In your case the solution would be to compare name property for each book:
BookRegistration.prototype.in_array = function(list, obj) {
for(i in list) { if(list[i].name === obj.name) return true; }
return false;
}
EDIT (related to your comment question):
I would rewrite process_new_books_list method as follows:
BookRegistration.prototype.process_new_books_list = function(data, callback) {
var i = 0,
self = this;
(function nextBook() {
var book = data[i];
if (!book) {
callback();
return;
}
self.process_author(book, function() {
i++;
nextBook();
});
})();
}
In this case next process_author is being called not immediately (like with _.each), but after callback is executed, so you have consequence in your program.
Not sure is this works though.
Sorry for my English, I'm not a native English speaker

accessing items in firebase

I'm trying to learn firebase/angularjs by extending an app to use firebase as the backend.
My forge looks like this
.
In my program I have binded firebaseio.com/projects to $scope.projects.
How do I access the children?
Why doesn't $scope.projects.getIndex() return the keys to the children?
I know the items are in $scope.projects because I can see them if I do console.log($scope.projects)
app.js
angular.module('todo', ['ionic', 'firebase'])
/**
* The Projects factory handles saving and loading projects
* from localStorage, and also lets us save and load the
* last active project index.
*/
.factory('Projects', function() {
return {
all: function () {
var projectString = window.localStorage['projects'];
if(projectString) {
return angular.fromJson(projectString);
}
return [];
},
// just saves all the projects everytime
save: function(projects) {
window.localStorage['projects'] = angular.toJson(projects);
},
newProject: function(projectTitle) {
// Add a new project
return {
title: projectTitle,
tasks: []
};
},
getLastActiveIndex: function () {
return parseInt(window.localStorage['lastActiveProject']) || 0;
},
setLastActiveIndex: function (index) {
window.localStorage['lastActiveProject'] = index;
}
}
})
.controller('TodoCtrl', function($scope, $timeout, $ionicModal, Projects, $firebase) {
// Load or initialize projects
//$scope.projects = Projects.all();
var projectsUrl = "https://ionic-guide-harry.firebaseio.com/projects";
var projectRef = new Firebase(projectsUrl);
$scope.projects = $firebase(projectRef);
$scope.projects.$on("loaded", function() {
var keys = $scope.projects.$getIndex();
console.log($scope.projects.$child('-JGTmBu4aeToOSGmgCo1'));
// Grab the last active, or the first project
$scope.activeProject = $scope.projects.$child("" + keys[0]);
});
// A utility function for creating a new project
// with the given projectTitle
var createProject = function(projectTitle) {
var newProject = Projects.newProject(projectTitle);
$scope.projects.$add(newProject);
Projects.save($scope.projects);
$scope.selectProject(newProject, $scope.projects.length-1);
};
// Called to create a new project
$scope.newProject = function() {
var projectTitle = prompt('Project name');
if(projectTitle) {
createProject(projectTitle);
}
};
// Called to select the given project
$scope.selectProject = function(project, index) {
$scope.activeProject = project;
Projects.setLastActiveIndex(index);
$scope.sideMenuController.close();
};
// Create our modal
$ionicModal.fromTemplateUrl('new-task.html', function(modal) {
$scope.taskModal = modal;
}, {
scope: $scope
});
$scope.createTask = function(task) {
if(!$scope.activeProject || !task) {
return;
}
console.log($scope.activeProject.task);
$scope.activeProject.task.$add({
title: task.title
});
$scope.taskModal.hide();
// Inefficient, but save all the projects
Projects.save($scope.projects);
task.title = "";
};
$scope.newTask = function() {
$scope.taskModal.show();
};
$scope.closeNewTask = function() {
$scope.taskModal.hide();
};
$scope.toggleProjects = function() {
$scope.sideMenuController.toggleLeft();
};
// Try to create the first project, make sure to defer
// this by using $timeout so everything is initialized
// properly
$timeout(function() {
if($scope.projects.length == 0) {
while(true) {
var projectTitle = prompt('Your first project title:');
if(projectTitle) {
createProject(projectTitle);
break;
}
}
}
});
});
I'm interested in the objects at the bottom
console.log($scope.projects)
Update
After digging around it seems I may be accessing the data incorrectly. https://www.firebase.com/docs/reading-data.html
Here's my new approach
// Load or initialize projects
//$scope.projects = Projects.all();
var projectsUrl = "https://ionic-guide-harry.firebaseio.com/projects";
var projectRef = new Firebase(projectsUrl);
projectRef.on('value', function(snapshot) {
if(snapshot.val() === null) {
console.log('location does not exist');
} else {
console.log(snapshot.val()['-JGTdgGAfq7dqBpSk2ls']);
}
});
$scope.projects = $firebase(projectRef);
$scope.projects.$on("loaded", function() {
// Grab the last active, or the first project
$scope.activeProject = $scope.projects.$child("a");
});
I'm still not sure how to traverse the keys programmatically but I feel I'm getting close
It's an object containing more objects, loop it with for in:
for (var key in $scope.projects) {
if ($scope.projects.hasOwnProperty(key)) {
console.log("The key is: " + key);
console.log("The value is: " + $scope.projects[key]);
}
}
ok so val() returns an object. In order to traverse all the children of projects I do
// Load or initialize projects
//$scope.projects = Projects.all();
var projectsUrl = "https://ionic-guide-harry.firebaseio.com/projects";
var projectRef = new Firebase(projectsUrl);
projectRef.on('value', function(snapshot) {
if(snapshot.val() === null) {
console.log('location does not exist');
} else {
var keys = Object.keys(snapshot.val());
console.log(snapshot.val()[keys[0]]);
}
});
$scope.projects = $firebase(projectRef);
$scope.projects.$on("loaded", function() {
// Grab the last active, or the first project
$scope.activeProject = $scope.projects.$child("a");
});
Note the var keys = Object.keys() gets all the keys at firebaseio.com/projects then you can get the first child by doing snapshot.val()[keys[0])

Resources