How can we broadcast updated data from factory.
Am having two factories and two controllers.
Service1: updates the data into db
app.factory('uac',function($http,$q, $cookies)
{
formData.uClient = function(data)
{
var client = $http.post('add_client/',data)
return $q.all({'response':client})
}
}
service 2: gets list of clients from db
app.factory('adService',function($http, $q)
{
clientData = {}
clientData.getListofClients = function()
{
var listOfClients = $http.get('get_clients/')
return $q.all({'listOfClients':listOfClients})
}
return clientData
})
controller 1: sends data to be updated to service
app.controller('acController', function($scope,uac)
{
$scope.clientDatadata = {name:"abcd"}
uac.uClient($scope.clientData)
}
controller 2: gets clients data from service
app.controller('getDetailsController',function($scope,adService)
{
adService.getListofClients().then(function(data)
{
$scope.clientList = data.listOfClients.data
console.log($scope.clientList)
},
function(error)
{
console.log("Can fetch data")
});
}
First I will call getDetailsController to get all clients and whenever a new client is added by calling "acController" i want the updated list of clients in "$scope.clientList".How can i get it done?
You can $emit on event from the sender controller and listen for it in the receiver controller. Since I'm not sure about the hierarchy of your controllers, I'll listen on $rootScope:
controller 1: sends data to be updated to service
app.controller('acController', function($scope,uac)
{
$scope.clientDatadata = {name:"abcd"}
uac.uClient($scope.clientData).then(function(res){
$scope.$emit('serviceUpdated', res); // you can send data if you wish
});
}
controller 2: gets clients data from service
app.controller('getDetailsController',function($scope,adService,$rootScope)
{
function getList(){
adService.getListofClients().then(function(data)
{
$scope.clientList = data.listOfClients.data
console.log($scope.clientList)
},
function(error)
{
console.log("Can fetch data")
});
}
getList();
$rootScope.$on('serviceUpdated', getList);
}
There are three ways to go for this:
(easy) Create a factory which will contain the list of clients and use it directly on your view.
-
myApp.factory('clientList', function () {
return [];
});
call controller 2 to get the list of clients and save on the factory:
app.controller('getDetailsController',function($scope,adService, clientList)
{
adService.getListofClients().then(function(data)
{
clientList = data;
},
function(error)
{
console.log("Can fetch data");
});
}
controller 1: sends data to be updated to service and saves the returned data on the clientList factory
app.controller('acController', function($scope,uac, clientList)
{
$scope.clientDatadata = {name:"abcd"};
clientList = uac.uClient($scope.clientData);
}
The issue about this solution is that in a complex app you wouldn't want to have the data exposed via a factory.
Implement getters and setters on the factory so you don't expose the data to the views but rather have them controlled by the controllers (using $watch).
Use $emit as explained on the answer by #Shomz. But, I would propose to make another get list request after the emission is received. I'd rather broadcast the updated data(in this case the response of the post/update request) and than update the variables that are bound to views.
Related
I have a side menu and its controller. In another controller I get the user data and want to update my menu items. How to achieve that ? I tried using $watch and services but to no success. Also I never used $rootScope so please avoid if it is not the only solution.
.controller('menuCtrl', function($scope) {
$scope.username = ""
})
.controller('afterloginCtrl', function($scope) {
var a = "this is username"
$scope.username = a // here I wish to update username in menuCtrl
})
Please let me if you need more code or details.
EDIT
Actually I update user info via database table, from their I am retrieving the username and other info. So menu controller needs to update with current values in database every time
You can to use some service for sharing data between controllers.
For example:
.service('currentUser', () => ({data: {}, someMethod: () => {}}))
.controller('Ctrl1', (currentUser, $scope) => {$scope.user = currentUser.data;})
.controller('Ctrl2', (currentUser) => {currentUser.data.name = 'username';});
Obviously, you can also extend your service with some appropriate methods. etc.
Then you can use these methods:
.controller('Ctrl2', (currentUser, api) => {
api.retrieveUser()
.then(user => currentUser.setData(user));
});
.service('loginuser', () => ({}))
.controller('fistCtrl', (currentUser, $scope) => {
$scope.data1 = loginuser;
})
.controller('secondectrl', (loginuser) => {
loginuser.name = 'sessionname';
})
If you are using service or factory data will be lost after page refresh.
You Can use browser session storage.
sessionStorage.setItem('NAME', "XYZ"); //set Data
sessionStorage.getItem('NAME'); // Get Data
//Afetr use you can clear data.
sessionStorage.clear();
You can use localStorage
Use the following code in your first controller:
localStorage.setItem($scope.username);
and the next controller:
$scope.username = localStorage.getItem();
As per the above comments your requirement is to persistent the username if user closes app and reopens.So, by using service or factory data will be lost once app reload or close.
Work around :
You can use localStorage to store username and can also access the data across controllers.
Code :
You can create a common factory service that will save and return the saved local storage data based on the key.
app.factory('storageService', ['$rootScope', function($rootScope) {
return {
get: function(key) {
return localStorage.getItem(key);
},
set: function(key, data) {
localStorage.setItem(key, data);
}
};
}]);
In controller :
Inject the storageService dependency in the controller to set and get the data from the local storage.
.controller('menuCtrl', ['$scope','storageService',function($scope,storageService) {
// Get local storage data from storageService
storageService.get('username');
}])
.controller('afterloginCtrl',['$scope','storageService',function($scope,storageService) {
var a = "this is username"
// Set local storage data to storageService
storageService.set('username', a);
}])
So this is my service that I use to fetch user details.
angular.module('app')
.factory('userDetailService', function($http) {
var userData = {};
function getUserDetails(userId) {
if (userId) {
return $http.get("/users/" + userId).success(function(data) {
angular.copy(data[0], userData);
});
}
}
return {
userData: userData,
getUserDetails: getUserDetails
}
})
Now in Controller 1 that uses this service, I have this bit of code which works fine as I get the relevant data.
$scope.getUserId = function(userId) {
if (userId) {
$scope.userData = userDetailService.userData;
userDetailService.getUserDetails(userId).success(function() {
console.log($scope.userData); //Prints valid user data
});
}
};
After this function executes in Controller 1, I try to do the following in Controller 2:
$scope.userData = userDetailService.userData;
console.log($scope.userData); //Prints null
But $scope.userData is null. Isn't the whole purpose of using a service to share data between controllers? Since I have already set the value of userData in Controller 1, shouldn't I be able to access it in Controller 2?
Weirdly enough, the modal dialog which is the template for Controller 2 is able to access data in the form of {{userData.first_name}} or {{userData.last_name}}. If this works, why is $scope.userData null? What am I missing?
Edit:
Template 1:
<div id="myModal" ng-controller="Controller 1">
<modal-configure-user></modal-configure-user>
<a data-toggle="modal" data-target="#configureUserModal" href="#" ng-click="getUserId(user.id)" data-id="user.id">{{user.first_name + ' ' +user.last_name}}</a>
</div>
Template 2:
<div ng-controller="Controller 2" id="configureUserModal">
</div>
Both are modal dialog windows.
Your approach is not very reliable, since you can't be 100% sure that data has already loaded when you try to access it in the second controller. Instead of assigning user data to variable always invoke getUserDetails method, which returns a promise. Then you just need to cache loaded data to avoid duplicated requests.
angular.module('app')
.factory('userDetailService', function($q, $http) {
var userData;
function getUserDetails(userId) {
if (userId) {
return userData ? $q.when(userData) : $http.get("/users/" + userId).success(function(data) {
userData = data;
return userData;
});
}
}
return {
getUserDetails: getUserDetails
}
});
Wrapping userData into $q.when creates a promise object, which resolves immediately. This is what you need, because service API is now consistent - you always deal with promises.
The usage in both controller then would be:
userDetailService.getUserDetails(userId).then(function(data) {
$scope.userData = data;
});
I have a question regarding angularJS services.
From what I have read, there are two ways of using services.
[1] Have a service return a promise to return data. If you use this method, in your routeProvider, you can make sure Angular resolves this promise to return data BEFORE it loads the page.
e.g.
App.factory('BooksService', function($q, $http) {
var deferred = $q.defer();
$http.get('/rest/books').then(function(data) {
deferred.resolve(data);
}, function(err) {
deferred.reject(data);
});
return deferred.promise;
};
Then, in my route provider:
...
$routeProvider.when('/books', {
controller : 'BooksCtrl',
templateUrl: '/partials/books.html',
resolve: {
books: 'BooksService'
}
});
...
Then, in my controller:
App.controller('AddPaypalAccountCtrl', function($scope, BooksService) {
$scope.books = BooksService;
}
[2] Have a service return an object that contains functions and data.
e.g.
App.factory('BooksService', function($q, $http) {
var books = [];
var service = {
getBooks : function() {
return books;
},
addBook: function(book) {
books.push(book);
}
};
return service;
};
My question: Is it possible to get the best of both worlds and have a service return a promise that when resolves returns an object that contains functions and data?
I want the $http call to get the books to be resolved before I load the '/books' page, BUT I also want access to a service that can manage said books. Of course I can write two separate services, but I wonder if it's more efficient to keep them both in the same service and write a service that kills two birds with one stone like so:
Here's an example of my factory that returns a promise for retrieving the books.
App.factory('BooksService', function($q, $http) {
var books = [];
var service = {
getBooks: function() {
return books;
},
addBook: function(book) {
books.push(book);
}
}
var deferred = $q.defer();
$http.get('/books').then(function(data) {
books = data.data;
deferred.resolve(service);
, function(err){
deferred.reject(err);
});
return service;
};
Then, as per before, my route provider is as follows, requiring that books be retrieved before I go to the /books page:
...
$routeProvider.when('/books', {
controller : 'BooksCtrl',
templateUrl: '/partials/books.html',
resolve: {
books: 'BooksService'
}
});
...
Then, in my controller, I will attach books to the scope like so.
App.controller('AddPaypalAccountCtrl', function($scope, BooksService) {
$scope.books = BooksService.getBooks();
}
I haven't seen anyone do this yet, so I'm wondering if this is OK.
I feel you are trying to break the SRP - Single Responsibility Principle.
What is the Responsibility of your service?
Provide an API for async request or make the request?
If it provides API, it should not be loaded async.
If too make the request, it should be a method of the service, not the service itself. A service is the interface to your request, not the request!
Rarely you may need to get logic back from your server, but again, you have to separate concerns:
Get the logic (e.g. Angular expression as a string) from server.
Parse into a function performing the logic (can be done with Angular $parse service).
Inject your logic function wherever you need to use it.
I'm using AngularJS to login a user (using Restangular). The login controller gets returned a 'Token' which I then need to use on every request thereafter.
My question is, what is the best way to store this Token using AngularJS? It needs to exist for the lifetime of the app.
I was looking at services but I have to keep injecting it into the controllers on to keep it alive.
lifetime is not promise as far as you are using web apps, but if you want you can use localstorage,
here is an example service how to use localstorage in angular, you can add it to service.js file:
var storeService = innovidServices.factory('storeService', function() {
var service =
{
setClientData:function(client_details)
{
window.localStorage.setItem( "client_data", JSON.stringify(client_details) );
client_data = client_details;
},
getClientData:function()
{
if (client_data == null)
{
client_data = JSON.parse(window.localStorage.getItem("client_data"));
}
return client_data;
}
}
var client_data = null;
return service;
});
I think the best way is store this 'Token' in the $rootScope.
myapp.controller('loginCtrl', function($scope, $rootScope) {
...
$rootScope.token = Token;
...
});
Then use http interceptor to inject this as for example GET parameter to every query
myapp.factory('httpTokenInterceptor', function ($rootScope) {
return {
request: function (config) {
var token = $rootScope.token;
if (token) {
config.url = URI(config.url).addSearch({'token':token}).toString();
}
return config;
}
};
});
myapp.config(function ($httpProvider) {
$httpProvider.interceptors.push('httpTokenInterceptor');
});
Maybe I'm going about this the wrong way, i'm not sure, but I have 2 services, one is a user service which gets a bunch of details about the user from the server, the other being one that relies on some user details from the user service and then makes some more calls to the server to get other information.
Anyway, because of the async stuff that goes on when the 2nd service makes the calls the information required from the user server has not yet been populated.
I know Angular services can depend on one another, but not in this context it would appear?
factory('User', ['$resource', function ($resource) {
return $resource(usersUrl, {}, {
//The data model is loaded via a GET request to the app
query: {method: 'GET', params: {}, isArray: false},
putupdate: {method: 'PUT', params:{}}
});
}])
.factory('UserData', function() {
var data = {}
data.userinfo = {};
if(data = {}){
}
return {
updateinfo: function(newdata) {
data.userinfo = newdata;
// alert(data.userinfo.user)
},
userinfo: data
}
})
.factory('PlansData', ['UserData', 'User', '$rootScope', function(userData, user, $rootScope) {
var data = {}
data.plansinfo = {};
//alert(userData.data.userinfo.user.email)
if(data = {}){
}
return {
updateinfo: function(newdata) {
alert(user.query())
data.plansinfo = newdata;
},
plansinfo: data
}
}])
So I have a user service and a caching userdata service, but if I ever try and call anything from UserData in the PlansData service I get undefined.
How do I get plansData to wait for UserData to have some data?
Thanks
Tom
I'm not sure what you're trying to accomplish, but this line of code:
if(data = {}){
}
In both your services is wiping out your data object. You're setting the whole data object to be {}