How to Create Multiple Instances of Angular Module - javascript

I have an app with the following basic structure,
weatherDisplayController.js
weatherGrabbingService.js
userColorPreferencesService.js
When the user changes their color preferences for viewing the weather, it is stored in userColorPreferencesService.js.
However, I want to add another view where you can view all your friends' dashboards, which means creating a new micro-instance of the module. However, when I do, they will overwrite the color preferences in the Service.
How can I have multiple instances of the same module on one page?

Thats because the services are singletons. there's is only 1 existing instance of it.
Maybe make the constructor within your colorservice as a oop class and store multiple of them in the service itself.
function ColorPreferences(){
//any data
}
app.service("userColorPreferencesService", function(){
this.ownColors = new ColorPreferences({ /* data goes here */});
this.buddyColors = [];
});
//in your controller, when sharing actived
userColorPreferencesService.buddyColor = new ColorPreferences({ /* data from ajax? */});

The only varying data should be on the scope for that part of the page. Instead of storing the data in the service you could store it in each scope and pass the required data to the service.

Related

How to pass Data from One Controller to Service and access that data on Other Controller on same event?

I'm facing some issue while trying to pass the data from one controller to the other using my service.
I've been implementing the prototype inheritance using the $rootScope in my controller and broadcasting that object, so that I can access that data in the other controllers.
As I'm using the $rootScope, I'm polluting the global namespace, I'd like to pass the data from this controller to the other.
I'm just displaying minimal data inside a table, when the user clicks on specific record of the table, I want to display entire data inside the object.
This is how I'm handling.
<tr ng-repeat="contact in filterContacts = (contacts.contactsData | filter:sensitiveSearch | orderBy:selectBox) " ng-style="{'background-color': contact.email == selectedContact.email ? 'lightgrey' : ''}" ng-click="selectContact(contact)">
In Controller A: I'm calling this function from view and injecting the specific row contact detail.
$scope.selectContact = function(contact) {
contactService.selectedContactInfo(contact);
};
In the Service I'm just returning that data -
var selectedContactInfo = function(contact) {
return contact;
};
How can I access this data in the Controller B during the same event and display it on my view.
Here is the reference Link: http://plnkr.co/edit/beOmiv?p=info
I don't want to use the $rootScope, but I'd like to access the data onto the other controller.
You could use an angular service for this.
The angular services are singletons and you inject them in controllers so you could inject the same service in two different controllers and you are effectively passing state between them.
Imagine you have a property in your service which could be called selectedUserID. You update this property when you click on the specific row. Then in the next controller, you inject the same service and use this property to determine which details you will load.
so you could have a method inside your service which could look like this :
updateSelectedUser = function(userID) {
this.selectedUserID = userID;
}
Your controller then calls this method from the service when the click action happens :
myService.updateSelectedUser($scope.selectedUserID);
This is just an example of course, put your own values in there.
One thing to keep in mind : services can hold state, they are objects after all and singletons so you always inject the same instance.
It makes sense to make sure that the state stored inside the service is not modified by outside actions which do not go through this service. In other words make sure that nothing else changes this selectedUserID so your service state data never gets out of sync. If you can do this, then you are golden.

What is the best practice for listing/editing items in Angular.js?

I have a list of employees in Employees view where I can add and a new employee and delete an existing one.
For listing the employees, I use an ng-repeat in the view and I can delete the particular employee by calling my splice method in controller with the passed Id and Index value.
and for editing, I use angular(copy) then update the edited value in DB. Everything is fine regarding to Add a new employee, edit and delete an employee.
I wrote all the codes in single view - single html and its too large in size. I used ng-show / ng-hide to perform add/edit actions.
I would like to break code to several views for add and edit the employee info and I use the same EmployeeController for all the CRUD operations in employee model.
If I break the employee.html to several views then I have to initialize the controller and ended up with losing the data. I can use $rootScope here to persist my data but it will pollute my structure as global is an evil thing.
Please suggest me a best practice to handle this situation. Thanks in advance!
Here is my code,
// To get all employee instances
$scope.getEmpList = function(){
$scope.employees = EmployeeFactory.getEmpList();
}
// Update employee details
$scope.editEmp = function(empObj){
$scope.updatedEmpObj = angular.copy(empObj);
EmployeeFactory.updateEmployee($scope.updatedEmpObj);
}
You should use a service to hold your data and inject that service into each controller where you need the data. A service is a singleton, and is instantiated only once.
See Using angular service to share data between controllers for more details.

Get current user from inside the model in Sails

I'm using toJSON() method of my model in Sails in order to control the visibility of some of it's properties, when model is exposed via application's API.
In order to decide which properties to display and which to omit I need to know the permissions of the current user. So, how do I get the current user from inside the model? Or is there a better way (pattern) to solve this problem?
Here's some sample code I want to achieve:
toJSON: function () {
var result = {};
result.firstName = this.firstName;
result.lastName = this.lastName;
// Exposing emails only to admin users.
if (currentUser.isAdmin()) {
result.email = this.email;
}
return result;
}
Your asking about reading a session inside the model call. Currently the way sails and waterline are built you can not do this.
You can use the select property on your initial model call to restrict the columns returned. Since this would be in the context of your controller you would have access to the req object.
Here are a bunch of related questions / answers on this topic.
sails.js Use session param in model
Is it possible to access a session variable directly in a Model in SailsJS
https://github.com/balderdashy/waterline/issues/556
https://github.com/balderdashy/waterline/pull/787
Sails Google Group Discussion on the topic

Angularjs Best Practice for Data Store

My angular app have 2 controllers. My problem is that the controllers does not keep the data when the user navigates away from the page.
How can I store the selected data on of my controllers into a data store so it can be used between other controllers?
Option 1 - custom service
You can utilize a dedicated angular service to store and share data between controllers (services are single instance objects)
service definition
app.service('commonService', function ($http) {
var info;
return {
getInfo: getInfo,
setInfo: setInfo
};
// .................
function getInfo() {
return info;
}
function setInfo(value) {
info = value;
}
});
usage in multiple controllers
app.controller("HomeController", function ($scope, commonService) {
$scope.setInfo = function(value){
commonService.setInfo(value);
};
});
app.controller("MyController", function ($scope, commonService) {
$scope.info = commonService.getInfo();
});
Option 2 - html5 localStorage
You can use the built-in browser local storage and store your data from anywhere
writing
$window.localStorage['my-data'] = 'hello world';
reading
var data = $window.localStorage['my-data']
// ...
check out this awesome project:
https://github.com/grevory/angular-local-storage
Option 3 - via web server api
If you need to persist data among different users, you should save it somewhere in the server side (db / cache)
function getProfile() {
return $http.get(commonService.baseApi + '/api/profile/');
}
function updateProfile(data) {
var json = angular.toJson(data);
return $http.post(commonService.baseApi + '/api/profile/', json);
}
EDIT See Jossef Harush's answer where he has written an in-depth response that covers other methods including this one.
I'd recommend using either localStorage or sessionStorage - http://www.w3schools.com/html/html5_webstorage.asp.
HTML local storage provides two objects for storing data on the client:
window.localStorage - stores data with no expiration date
window.sessionStorage - stores data for one session (data is lost when the browser tab is closed)
This assumes that you don't want to POST/PUT the data to your web service (windows service mention in your question).
If you data is an array or some sort, you can convert it to JSON to store as a string and then when you need it you can parse it back as follows - How do I store an array in localStorage?:
var names = [];
names[0] = prompt("New member name?");
localStorage["names"] = JSON.stringify(names);
//...
var storedNames = JSON.parse(localStorage["names"]);
There is an option not mentioned in other answers (AFAIK).
EVENTS
You can use events for communication between controllers.
It's a straightforward communication that doesn't need a mediator
(like service) and can't be wiped by the user (like HTML storage).
All the code is written in controllers that you are trying to
communicate with and thus very transparent.
A good example how to leverage events to communicate between controllers can be seen below.
The publisher is the scope that wanna publish (in other words let others know something happened). Most don't care about what has happened and are not part of this story.
The subscriber is the one that cares that certain event has been published (in other words when it gets notified hey, this happened, it reacts).
We will use $rootScope as a mediator between publisher and a subscriber. This always works because whatever scope emits an event, $rootScope is a parent of that scope or parent of a parent of a parent.. When $rootScope broadcasts (tells everyone who inherits) about an event, everyone hears (since $rootScope is just that, the root of the scope inheritance tree) so every other scope in app is a child of it or child of a child of a child..
// publisher
angular.module('test', []).controller('CtrlPublish', ['$rootScope','$scope',
function ($rootScope, $scope) {
$scope.send = function() {
$rootScope.$broadcast('eventName', 'message');
};
}]);
// subscriber
angular.module('test').controller('ctrlSubscribe', ['$scope',
function ($scope) {
$scope.$on('eventName', function (event, arg) {
$scope.receiver = 'got your ' + arg;
});
}]);
Above we see two controllers communicating a message to each other using an event. The event has a name, it has to be unique, otherwise, a subscriber doesn't differentiate between events. The event parameter holds autogenerated but sometimes useful data, the message is the payload. In this example, it's a string but it can be any object. So simply put all the data you wish to communicate inside an object and send it via event.
NOTE:
You can avoid using root scope for this purpose (and limit the number of controllers that get notified of an event) in case two scopes are in direct inheritance line of each other. Further explanation below:
$rootScope.$emit only lets other $rootScope listeners catch it. This is good when you don't want every $scope to get it. Mostly a high level communication. Think of it as adults talking to each other in a room so the kids can't hear them.
$rootScope.$broadcast is a method that lets pretty much everything hear it. This would be the equivalent of parents yelling that dinner is ready so everyone in the house hears it.
$scope.$emit is when you want that $scope and all its parents and $rootScope to hear the event. This is a child whining to their parents at home (but not at a grocery store where other kids can hear). This is a shortcut to use when you wanna communicate from the publisher that is a child or n-th child of the subscriber.
$scope.$broadcast is for the $scope itself and its children. This is a child whispering to its stuffed animals so their parents can't hear.
EDIT: I thought plunker with a more elaborate example would be enough so I decided to keep is simple here. This elaborate explanation should be better.
To share data between two controllers on the same page, you can use factories/services. Take a look at Share data between AngularJS controllers for example.
However, if this is across page reloads/refreshes, you will need to store the data in local storage and then read it upon reloading. An example of that is here: How do I store data in local storage using Angularjs?
Checkout this library https://www.npmjs.com/package/angularjs-store
This can help you manage your application state much simpler as it will force you to have a one way data flow on your application.

Javascript object persistency in Meteor js

Supposed that I created a js object like this
Cart = function Cart(){
this.contents = new Array();
}
Cart.prototype = {
add : function(obj){
this.contents.push(obj);
}
}
and put it in the project lib folder so that this object can be used project-wide.
Is it possible to use this object persistently in the template backend (js file)? For example, I declared :
Template.pageA.rendered = function(){
var smallCart = new Cart();
}
Can I use it in the template event? For example:
Template.pageA.events = {
'click button#add' : function(event){
smallCart.add('newItem'); //Is this object as same as the one in rendered?
}
}
I have been doing stuff by using Sessions but when there are a lot of operations to be done, the events will be cluttered with business logics and calculations. I want to avoid this, thus I am thinking of putting the logics into Javascript object functions.
Will this approach work? Will the object stay persistent?
You can place that code inside a Meteor.startup, so the object will persist always.
Meteor.startup(function(){
Cart = function Cart(){
this.contents = new Array();
}
Cart.prototype = {
add : function(obj){
this.contents.push(obj);
}
}
})
Is this object as same as the one in rendered? yes
From my point of view, putting the class definition of your cart into the lib folder can be good. If it's client-side code only, you can store it into a cart.js file into your client folder.
This definition will be available on client.
Then you need to store an instance of your cart. Instanciate a cart into your template can be a good idea (you don't need it before that), but your need to store a reference into you client side application scope (usually into app.js at the root of your client folder for example)
client/cart.js
Cart = function() {}....
client/app.js
myCart = null;
Into your template, you can instantiate the cart
myCart = new Cart();
If you're using your only on one template, you can store the instance directly to the template into the controller logic. Attach it to the rendered of the template is a bit wired, because you usually handle template rendering logic.
This way, a cart is going to be created for every user which come on your site and called the template. If you have a multi page app using iron router, myCart will be available whatever the page as soon as you have already create it into your template.
Be aware, in this way, if the client "refresh" the page reloading the JavaScript code, you'll loose the current cart. You need to persist the cart on local storage in order to support the refresh. Not worth in all the case, but for a shopping cart, can be usefull
Putting the Cart code in lib is correct, it will be available everywhere.
As far as smallCart is concerned you have declared it inside a function so it isn't available to the other template function.
Declare it outside the two functions and it will be available to both.
var smallCart;
Template.pageA.rendered = function(){
smallCart = new Cart();
}
Template.pageA.events = {
'click button#add' : function(event){
smallCart.add('newItem'); //Is this object as same as the one in rendered?
}
}

Categories

Resources