Best way to keep and share huge data between controllers - angularjs

The App I'm building is complex and the data is huge. It is an order administration dispatch app.
At the beginning the first thing I have to do it to get from the API all the orders, all the stores and all the couriers.
During the life cycle of an order I receive several updates from the API and I handle everything through Server Sent Events.
Once I load the data I put everything in 3 different $scope: $scope.orders, $scope.stores and $scope.couriers and a I created a service to handle and share updated data between controllers.
One of the service look like this:
App.factory('Orders', ['logs', function (logs) {
var orders = {};
var newOrder = null;
var updatedOrder = null;
orders.list = [];
orders.getNewOrder = function () {
return newOrder;
};
orders.setNewOrder = function (order) {
newOrder = order;
};
orders.updateOneOrder = function (index, order) {
orders.list[index] = order;
updatedOrder = order;
};
orders.getUpdatedOrder = function () {
return updatedOrder;
};
orders.getAllOrders = function () {
return orders.list;
};
return orders;
}]);
And I $watch for those methods to see changes and update the relative $scope.
As you can see in my service I have one more Array to store data in order to be able to handle it due to the fact I can not use $scope in a Factory.
I followed a guide online regarding this. I know there is also Broadcast and Emit but probably it was too complex to handle all those things with that method.
One more thing I avoided is to store orders, stores and couriers in $rootScope.
I think the App is a bit slow and use a lot of memory to handle everything so I wish to know the best practice to handle huge data that need to be shared between controllers and updated in real time...
I think my method at the moment is using too much memory.

Related

Angular - reusing code (service or controller)

I'm using Angular to develop commenting functionality for a web app.
Currently there are two sections in the application were a user can comment:
Category
Product
About 90% of the commenting functionality is the same for both sections and as such I would like to make this reusable - i.e write some service or controller that I can reference/use as a base.
So far, my research seems to point to using a factory service but unfortunately this doesn't seem to work (I've spent the whole day running through various tutorials).
It is quite possible that I am over thinking this and making it far too complicated but I honestly don't know which way to turn anymore.
Herewith a quick and dirty overview of what I have so far:
HTML view for the category
Controller for the category (receives data from service and posts data to service in order to bind data to model)
Service for the category (retrieve and stores all the necessary
data)
The product uses the same logic and a lot of the code in the service and controller will be duplicated.
I've merged the two services into one service successfully but I'm having trouble doing the same for the controller.
Do I:
Write a base controller that will communicate with the above mentioned service and that will hookup with the two existing controllers
OR
Write a factory/provider service that hooks up to the two existing controllers as well as the above mentioned service.
If you go the route of using a factory, you could put all the common functionality into its return object and reference that from your controllers.
Factory
angular.module('myModule').factory('CommonFunctions', function(){
return {
foo : foo,
bar : bar
}
function foo(){
console.log('foo');
};
function bar (){
console.log('bar');
};
}
Controller
angular.module('myModule')
.controller('myController', ['CommonFunctions', function(CommonFunctions) {
var vm = this;
vm.foo = CommonFunctions.foo();
vm.bar = CommonFunctions.bar();
}
angular's separation of service types ie:
for specific values
constant
value
(constant for specific values needed before other services are created)
for functions
factory
service
provider
(provider for specific instances when you need a services before other services are created, usually taking advantage of constants)
allow the ability to share data and ways to process that data between controllers and or directives, anything that can be a value can also be a constant, the only difference there being where they can be injected. Similarly any service can be rewritten to a factory or a provider, it is more your specific use case / what your more comfortable writing that would determine which to use, but really the best way to think about it is if you have a value that needs to be shared but is not needed inside angular.module.config then use value, otherwise use constant, now if you have a single function that you want to share, (maybe it processes that value in some way or maybe it just does something else) you should write it as a factory, then when you have a few of those factory's that deal with either that value, or anything else, you can combine them into a service or configure and combine them using a provider. here is a simple example (note i am using the recommended syntax for writing angular services):
'use strict';
var app = angular.module('test.app',[]);
app.constant('configureableValue',{defaultValue:55});
app.value('editableValue',{defaultValue:100,editedValue:null});
app.provider('configureValue',configureValueProvider);
configureValueProvider.$inject - ['configureableValue'];
function configureValueProvider(configureableValue){
var defaultVal = configureableValue.defaultValue,
originalVal = defaultVal;
return {
getValue:getValue,
setValue:setValue,
resetValue:resetValue,
'$get':providerFunc
};
function getValue(){
return defaultVal;
}
function setValue(val){
defaultVal = val;
}
function providerFunc(){
return {
get:function(){ return getValue(); },
reset:function(){ resetValue(); }
};
}
function resetValue(){
defaultVal = originalVal
}
}
// this factory is an example of a single function service, this should almost always be defined as a factory
app.factory('getEditableValue',getEditableValue);
getEditableValue.$inject = ['editableValue'];
function getEditableValue(editableValue){
return function(){
return editableValue.editedValue ? editableValue.editedValue : editableValue.defaultValue;
};
}
// same with this one
app.factory('setEditableValue',setEditableValue);
setEditableValue.$inject = ['editableValue'];
function setEditableValue(editableValue){
return function(val){
editableValue.editedValue = val;
}
}
// now this is an example of a service service collecting the factorys for an object with all the related behavior we need
app.service('editableService',editableService);
editableService.$inject = ['getEditableValue','setEditableValue'];
function editableService(getEditableValue,setEditableValue){
var self = this;
self.setVal = setEditableValue;
self.getVal = getEditableValue;
}
app.config(appConfig);
appConfig.$inject = ['configureValueProvider'];
function appConfig(configureValueProvider){
configureValueProvider.setValue('i changed '+ configureValueProvider.getValue() +' to this!!!!');
}
app.run(appRun);
appRun.$inject = ['configureValue','editableService'];
function appRun(configureValue,editableService){
console.log('before editing: ',editableService.getVal());
editableService.setVal('changed!!!');
console.log('after editing: ',editableService.getVal());
console.log('we changed this in the config func: ',configureValue.get());
configureValue.reset();
console.log('and now its back to the original value: ',configureValue.get());
}
i know thats a lot for a simple example, but there are a lot of features provided by angular, and many ways to use them, hopefully this helps.

How to handle user model in an AngularJS application?

I am refactoring an AngularJS application, and there, almost everything is stored in the $rootScope.
In my old applications I've build with Angular, I created a Service for each model, and then instantiated it within a Controller when needed.
I ask myself: Is it ok to store the whole user object inside the
$rootScope or what is best practice here?
How can I get sure to create a user during login and then pass it around throughout the whole application?
It seems ok generally storing user like models in $rootScope. But In my opinion it's not a best practise in angularjs(However I have used $rootScope way before).
Factory is the one of angularjs' beauty. Generally we use it to call rest services. But also you can create a model with it. Also you will be able to extend your model easily with injecting another model. That's just an idea , may be there is another better options to use model like objects in angularjs.
Lets look an example
// User Model
app.factory('User', function() {
var User = function(username) {
this.username = username;
this.email = null;
};
User.prototype.getDetails = function() {
var self = this;
return self.username;
};
return User;
});
// ExtendedUser Model
app.factory('ExtendedUser', function(User) {
var ExtendedUser = function() {
User.apply(this, arguments);
};
ExtendedUser.prototype = new User();
function getEmail() {
var self = this;
// You can make an http call to get email like information as an extra
self.email = "email#email.com";
return self;
}
ExtendedUser.prototype.getDetails = function() {
var self = this;
var extendedUser = getEmail();
return extendedUser;
};
return ExtendedUser;
});
I would look into a0-angular-storage: https://github.com/auth0/angular-storage It is great for storing user information / tokens or whatever to be retrieved throughout your app.
Key Features
Uses localStorage or sessionStorage by default but if it's not available, it uses ngCookies.
Lets you save JS Objects, If you save a Number, you get a Number, not a String
Uses a caching system so that if you already have a value, it won't get it from the store again.
If you don't want to store user model in $rootScope you can use private JS variable, which can be accessed by some service in Angular(since both factory and a service are singleton in Angular).
The nice addition is that it is harder to determine where the user model is stored, the only thing you need is proper encapsulation and code structure.

Accessing factories in the same Angular module

In my Angular app, I have some resource modules, each containing some cache factories.
For example,
projectRsrc.factory('getProjectCache', ['$cacheFactory', function($cacheFactory){
return $cacheFactory('getProjectCache');
}]);
I have a few of these to cache values received from the servers.
The problem is that at times I'd like to clear all the caches. So I want to put all the cacheFactories into one CacheCentralApp module and delete all the caches with a single call.
The trouble is, I don't know of any way to access other factories inside my module. So for instance, if I create a module CacheCentralApp, and in it, declare factories that provide cacheFactorys, how can I create a function in there that calls removeAll() on every cacheFactory?
I don't think it is possible to target all the factories of a certain module. I think however that another solution to your problem is to send a event that all factories has to be cleared. This will prevent that you will have to loop through all your factories and call a .clear() function on everyone.
You could send a event request with the following code:
$scope.$broadcast('clearAllCaches');
And listen to this event in every factory with:
$scope.$on('clearAllCaches', function() {
clearCache();
}
In a separate module you might create a factory for that:
var cacheModule = angular.module('CacheCentralApp', []);
cacheModule.factory('MyCacheFactory', ['$cacheFactory', function($cacheFactory) {
var cacheKeys = [];
return {
clearAll: function() {
angular.forEach(cacheKeys, function(key) {
$cacheFactory.get(key).removeAll();
});
},
get: function(key) {
if(cacheKeys.indexOf(key) == -1) {
cacheKeys.push(key);
return $cacheFactory(key);
} else {
return $cacheFactory.get(key);
}
}
}
}]);
To create new or get existing Cache you simply call MyCacheFactory.get(cacheName). To clear all the caches ever created in the factory you call MyCacheFactory.clearAll().
Note: I am not quite sure that Array.indexOf is available in every browser, you might want to use Lo-Dash or another library to make sure your code works.

Passing Data Between Pages in AngularJS + Page Refresh

I am trying to pass data between pages in the checkout process of an app but it's not working the way it should. I have done some reading and most people recommend using a service, but the only issue is that when the page is refreshed (user clicks refresh or comes back at a later time) all the data from the service disappears. This makes sense since the data in a service is not meant to be persistent but it is causing a problem.
So the question is: how can I pass data between pages in angularJS and still keep the data that was passed after a page refresh?
Here is my code so far (with my attempt at using query strings):
.service('checkoutService',
function checkoutService($location, Address, $routeParams, TicketGroup) {
var ticket_quantity = 0;
var ticket_group = {};
var selected_address = {};
this.loadInformation = function() {
if(!ticket_quantity && $routeParams.ticket_quantity)
ticket_quantity = $routeParams.ticket_quantity;
if(!ticket_group && $routeParams.ticket_group_id)
ticket_group = TicketGroup.get({id: $routeParams.ticket_group_id});
if(!selected_address && $routeParams.address_id)
selected_address = Address.get({id: $routeParams.address_id});
}
this.setTicketQuantity = function(quantity) {
ticket_quantity = quantity;
$location.path().search({ticket_quantity: quantity});
}
this.getTicketQuantity = function() {
return ticket_quantity;
}
this.setTicketGroup = function(object) {
ticket_group = object;
$routeParams.ticket_group = object.id;
}
this.getTicketGroup = function() {
return ticket_group;
}
this.setSelectedAddress = function(object) {
selected_address = object;
$routeParams.address_id = object.id;
}
this.getSelectedAddress = function() {
return selected_address;
}
});
There are several options to do this,
For smaller data sets you could use the $cookieStore, for data that is under 4k
Another option, especially with large data sets, would be to use Local Storage and then retrieve the data on page load/reload.
if it is only a very small amount of data, or data that is used through out multiple page you could use $rootscope, but this is not the best option as it just like polluting the global name space.
The last option, depending on how the data is retrieved, a service could be implemented, that is basically a singleton that can be passed to various angular scope.
Note: only the first two are persistent.
In your case I think that using local storage or the cookiestore will be you best options. You are trying to use a service, which would be appropriate if you did not want it to be persistent (leaving the page or a page refresh). Services are singletons that being managed by angular, when injected you will get a reference to the same object in each injection. However, when returning to the page this singleton will need to be re initialized, thus losing all previous data. The only way to make make a service persistent would be to load the data from a database, a local file, or noSQL from elsewhere. However, I do not think this is really what you are after.
If you are interested in pursuing the local storage implementation then look into these modules angular-local-storage, ngStorage or this answer
If you want to use the cookiestore look into this answer
You can use Session/LocalStorage. Or a browser db like pounchdb.
Seesion storage: store session data;
LocalStorage: store data not just session scope
pounchdb : offline db

Communicating between a Multiple Controllers and a directive

I have a directive that powers an HTML5 Canvas visualization. This directive has a wide array of methods to modify different parts of the visualization. The issue is that multiple controllers that have a different parent/child/sibling relationship need to communicate to this directive. Right now I have it wired this pretty awful way of emitting events up to the parent controller of the directive and then broadcasting them to the directive.
I have heard of using a service to do something like this, but nothing really explain why. I thought of using something like this:
angular.service('CanvasCommunication', function($rootScope) {
this.canvasAction = function() { $rootScope.broadcast('canvasAction'); };
}
And then have listener in the canvas to actual execute that action. This service could be then injected into any controller that communicates with the canvas.
The issue with this is that $rootScope.broadcast() has terrible performance and I want to make sure this communication channel is built in the most efficient way.
Has anybody dealt with something like this and thought of something better?
I've had the same issue - controllers needing to interact with each other, different parts of the app sending messages to each other, etc. In my projects, I've implemented a MessageService. Here's a very basic version of one (but honestly more than sufficient):
module.factory('MessageService',
function() {
var MessageService = {};
var listeners = {};
var count = 0;
MessageService.registerListener = function(listener) {
listeners[count] = listener;
count++;
return (function(currentCount) {
return function() {
delete listeners[currentCount];
}
})(count);
}
MessageService.broadcastMessage = function(message) {
var keys = Object.keys(listeners);
for (var i = 0; i < keys.length; i++) {
listeners[keys[i]](message);
}
}
return MessageService;
}
);
You might want to have listeners registered for particular subjects, and filter messages by subject, or not. Mine also queue messages on subjects until they're cleared, so that the messages can be viewed when a new view loads (in order to pair for ex. 'Success - Saved file' with a page change).

Resources