Setting and getting data value from service in Angular JS - javascript

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

Related

How to make $scope value in another controller update when calling function inside factory?

I'm having 2 controller placed on a same page and using a same factory. All things i want is when a function in controller 1 execute, it will call to the function inside factory then the $scope in controller 2 will be update its value. When page is loaded controller can get the list but after controller 1 call the factory, nothing was changed, no any call to server...
Here is Controller 1:
app.controller('controller1', function ($scope, $http, globalServices) {
$scope.createFuntion = function(){
$http.post(url, $.param(some_object)).then(function(response){
//Handle something ...
globalServices.userList();
});
}});
Here is Controller 2:
app.controller('controller2', function ($scope, $http, globalServices) {
$scope.users = globleServices.userList();});
Here is factory:
app.factory('globalServices', function ($http) {
return{
userList: function(){
var users_data = [];
$http.get(url).then(function (response) {
var res = response.data;
if (res.status === 200) {
angular.forEach(res.data, function (staff) {
users_data.push(staff);
});
} else {
alert('Oops! Somethings went wrong!');
}
});
return users_data;
}
}});
There is a thing in the AngularJs space and JavaScript in general referred to as the dot rule. If you have a property on an object like
service.data
when you assign that to another object
$scope.data = service.data;
It assigns a reference to the object and now if you update the service the controller does not know about the new data.
Using the dot rule you can have an object on the service that holds data objects
service.data = {};
this object should never change reference to a new object and always be the same instance and you can add new properties to it
service.data.userList = response.userList;
Now if you assign the data in the service to the scope
$scope.data = service.data;
and in the template use
<div ng-repeat="user in data.userList">{{ user.name }}</div>
Userlist will be updated when the service updates the userList.
You should never inject $http into controllers, you should only inject services into controllers and have services make http calls. Injecting $scope is an outdated method of doing AngularJs, you are following outdated tutorials and should look into using the controllerAs syntax or use components that wrap the controllerAs syntax with an Angular 2 style of development.
Create an object in your factory that will somehow serve as a state then create a getter for it. Separate your fetch function and getUserList. See the modified code below.
app.factory('globalServices', function ($http) {
var list = {
users_data: []
}
return{
getUserList: getUserList,
fetchUserList: fetchUserList
}
function getUserList() {
return list;
}
function fetchUserList() {
list.users_data = [];
$http.get(url).then(function (response) {
var res = response.data;
if (res.status === 200) {
angular.forEach(res.data, function (staff) {
list.users_data.push(staff);
});
} else {
alert('Oops! Somethings went wrong!');
}
});
}
});
Now in your controller1
app.controller('controller1', function ($scope, $http, globalServices) {
$scope.createFuntion = function(){
$http.post(url, $.param(some_object)).then(function(response){
//Handle something ...
globalServices.fetchUserList();
});
}});
and in your controller2
app.controller('controller2', function ($scope, $http, globalServices) {
$scope.users = globalServices.getUserList();
});
Now your $scope.users listen to every change in your user_data.
Access the array thru $scope.users.users_data

Update $scope value from another controller

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

How to access sub objects of an object returned by AngularJS data service inside controller

I have an angularjs data factory that makes a server call and sets the returned data in the factory returned object like this
angular.module('app').factory('DataService', function() {
// Returning object
return {
data: {},
isLoaded: false,
getData:getdata
};
function getData() {
var self = this;
//Some Ajax Call returns an 'dataObject'
self.data = dataObject; //dataObject has a few subObjects and list
}
});
Now inside my angularjs controller I am making call to the DataService.getData() to make ajax call and hence set the returned dataObject within factoryobject 'data'
and I have declared it in controller like this
appV.data = DataService.data;
If I try to print it on console or access like this
console.log(appVm.data), I am able to access it, but if I try to access any subObjects like Ex - appVm.data.property1 or appVm.data.subObject1.subObject1.property, it gives undefined.
Can someone please explain why this behavior is occuring ?
Thanks in Advance
It is just example:
app.factory("messageService", function($q){
return {
getMessage: function(){
return $q.when("Hello World!");
}
};
});
And now the routing configuration that will use the service in a resolve.
$routeProvider
.when("/news", {
templateUrl: "newsView.html",
controller: "newsController",
resolve: {
message: function(messageService){
return messageService.getMessage();
}
}
})
These link may help you:
Link 1,
Link 2

AngularJS: how should I set the params for $http dynamically?

I am very new with AngularJS. Thank you for answer. My code is as follow:
mainModule.controller('MainController', function($scope, $http) {
$http.get('http://localhost/backend/WebService.php', {params: {entity: 'IndexPageEntity'}}).
success(function(data) {
$scope.intro = data[0].IndexPageContent;
});
$http.get('http://localhost/backend/WebService.php', {params: {entity: 'ExhibitionServiceEntity'}}).
success(function(data) {
$scope.exhibit = data[0].ExhibitionServiceContent;
});
$http.get('http://localhost/backend/WebService.php', {params: {entity: 'ShootingServiceEntity'}}).
success(function(data) {
$scope.shooting = data[0].ShootingServiceContent;
});
});
My html file would be:
<div ng-controller="MainController">
<div>{{intro}}</div>
<div>{{exhibit}}</div>
<div>{{shooting}}</div>
</div>
I believe there must be some ways to improve the above code in order to reduce repetition. What I want is to pass entity parameter to the controller on creation.
Using ng-init to pass parameter is discouraged, according to the documentation. Writing custom directive to pass argument to scope does not work since parameters would be overwrittern.
What is the best practice to set params dynamically for use in $http? Thank you.
You should move all the logic to a service and use a directive. I would suggest you to modify your backend to return the same structured data, instead of IndexPageContent, ExhibitionServiceContent, etc. it should be Content or whatever name you want to use. But for now I've added a replace function to get the name of the content from the name of the entity.
mainModule.factory('webService', function($http) {
var apiUrl = 'http://localhost/backend/WebService.php';
function getContent(params) {
var config = {
'params': params
};
return $http.get(apiUrl, config);
};
return {
getContent: function(params) {
return getContent(params)
}
};
});
mainModule.controller('MainController', function($scope, webService) {
var params = {
'entity': $scope.entity
};
var contentName = $scope.entity.replace('Entity', 'Content');
webService.getContent(params).then(function (data) {
$scope.content = data[0][contentName];
});
});
mainModule.directive('EntityContent', function() {
return {
controller: 'MainController',
replace: true,
restrict: 'E',
scope: {
entity: '#entity'
},
template: '<div>{{ content }}</div>'
};
});
<div>
<entity-content entity="IndexPageEntity">
<entity-content entity="ExhibitionServiceEntity">
<entity-content entity="ShootingServiceEntity">
</div>
Create an object data and send the value for the key inside the object at every call.. Also pass the value for key to be set inside the scope..
E.g.
$scope.makeHttpCall = function(data) {
$http.get('http://localhost/backend/WebService.php', {params: data}).
success(function(data) {
$scope[$scope.key] = data[0][$scope.key];
});
};
you can then call this function as
$scope.key = 'IndexPageContent';
data = {
entity : 'yourValueHere'
};
$scope.makeHttpCall(data);
You can set other values as well inside the scope that are dynamic for each request..
I hope this makes sense to you...

How to use AngularJS to store a 'Token' for the lifetime of the app?

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

Categories