Scope variable value is undefined outside resource service in angularjs - javascript

I have created angularJs factory service to dealing with REST calls.Service is worked fine.I have some cases that i need to set values in to $scope.variable and access them in outside of resource service.But i got undefined. I wrapped them inside angularJs $q but seems like i did some mistake.please help me to solve this.
AngularJs Factory
myApp.factory('MyService',function($resource) {
return{
GetMYData:$resource(my rest service URL, {});
}
});
In Controller
$scope.myMap={};
$scope.promises=[];
$scope.myData = MyService.GetMYData.query();
$scope.myData.$promise.then(function (result) {
result.forEach(function(e, i) {
//Do something
var deferred = $q.defer();
$scope.myMap[e.key]=e; //Put elements into map and i tried to access map from outside service by giving key
deferred.resolve(result);
$scope.promises.push(deferred.promise);
$scope.myMap[$location.search()['dataID']] //This display the actual value
})
...
}
If URL parameter present in the URL i do the following things.
if($location.search()['dataID']){
$q.all($scope.promises).then(
function(data) {
console.log($scope.myMap[$location.search()['dataID']]) // throws undefined
},function(response) {
//Handle if promises are rejected
})
}
Please let me know how can i access the map values set by angularJs resource service from outside of service.

What I'll try to do is:
myApp.factory('MyService',function($resource) {
var _resource = $resource(my rest service URL, {},{
query:{method:"GET",IsArray:true}
})
return{
GetMYData:function(){
return _resource.query();
}
}
});
then:
$scope.myMap={};
$scope.promises=[];
MyService.GetMYData.$promise.then(function (result) {
result.forEach(function(e, i) {
//Do something
$scope.myMap[e.key]=e; //Put elements into map and i tried to access map from outside service by giving key
$scope.myMap[$location.search()['dataID']] //This display the actual value
})
...
}
and then access simply your
if($location.search()['dataID']){
console.log($scope.myMap[$location.search()['dataID']]) );
}

Related

how to make a function reusable between two or more controllers in angularjs

I have a question as I am new to angularjs. I have searched alot but unable to understand the concept. I have a basic concept that there should be one controller for one view but we can have multiple controllers for a single view. I have below two controllers in a single view and there is one function let say for example addComma function which I have to use in both controllers but I want to write it one time to make it reusable between all controllers let say 5,6 controllers on the same view. So basically the question is how to make a function in a controller global between all controllers of same view or other views so that I can use it anywhere in my application. Appologies if it is a dumb question I am having a hard time understanding the concepts of angularjs.
app.controller("GetChargesController", function ($scope, GetService, $rootScope) {
$scope.Title = "Charges Details List";
$rootScope.loading = true;
// Calling Serivce Method here
$scope.GetChargesDetails = GetService.GetAll("CommonApi", "GetChargesDetails").then(function (d) {
$scope.ChargesDetails = d;
$rootScope.loading = false;
});
// add comman function goes here
$scope.addComma = function(x) {
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
}
});
app.controller("GetPaymentsController", function ($scope, GetService, $rootScope) {
$scope.Title = "Payments Details List";
$rootScope.loading = true;
// Calling Serivce Method here
$scope.GetPaymentsDetails = GetService.GetAll("CommonApi", "GetPaymentsDetails").then(function (d) {
$scope.PaymentsDetails = d;
$rootScope.loading = false;
});
// add comman function goes here
$scope.addComma = function (x) {
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
}
});
Below is the generic service I've written to get any kind of data from database(using asp.net web api). As I've read that angular services can hold data and we don't need to call the database again and again when we are moving back and forth on links let's say for example I have these 3 links on a page. Home Link, PaymentCharges Link, Orders Link. so Home view will open by default. And when I click on PaymentCharges Link the call will go get data from database and render its view but when I click back on Home Link there should be no call going to database to get the data for the home page or when I click forward again on PaymentCharges Link the second time there should be no call going to database but what I see on firebug console option it is calling the function and going to database to get the data.I think we need to use cache to hold the data to not send the call to database.
app.factory("GetService", function ($http) {
var thisService = {};
// get all data from database
thisService.GetAll = function (controllername, methodName) {
var promise = $http({
method: 'GET',
url: '/api/'+controllername + '/' + methodName
})
.then(function (response) {
return response.data;
},
function (response) {
return response.data;
});
return promise;
};
});
Create an utils service and add the addComma function there. Inject the utils service and reuse the addComma in the controllers
app.factory('utils', function() {
return {
addComma: function() {}
}
});
app.controller("GetChargesController", function ($scope, utils) {
$scope.addComma = utils.addComma;
});
app.controller("GetPaymentsController", function ($scope, utils) {
$scope.addComma = utils.addComma;
});
Several ways to manage the service issue:
Re-use the promises by storing them in the service:
// create an object to store promises
var promises = {};
thisService.GetAll = function(controllername, methodName) {
promises[controllername] = promises[controllername] || {};
// if the particular promise doesn't already exist create it as a property of above object
if (!promises[controllername][methodName]) {
promises[controllername][methodName] = $http({...}).then(...;
}
// now return the specific stored promise
return promises[controllername][methodName];
};
or store the data and use $q to return a different promise that resolves with the stored data when it already exists. Make sure to inject $q for this approach
var data = {};
thisService.GetAll = function(controllername, methodName) {
data[controllername] = data[controllername] || {};
if (data[controllername][methodName]) {
// data exists for this controller and method, no need for new request
return $q.resolve(data[controllername][methodName]);
} else {
return $http({...}).then(function(response) {
// store the data for next use
var newData = response.data;
data[controllername][methodName] = newData;
return newData;
});
}
};
Why don't you just store the data in the service?
You could do:
.then(function (resp) {
thisService.controllerName.data = resp.data
}
Then you can reference the variable in your code using GetService.controllerName.data
Instead I would suggest you to use something like this which might make it little more generic. Check the below code
appResources-Factory will contain all the resources that are needed to be exposed.
indexServices-Factory will contain all your services corresponding to your index controller.
(function () {
use strict';
var commonServices = angular.module('commonServices', ['ngResource']);
commonServices.factory('appResources', ['$resource',
function ($resource) {
var indexResource=$resource('/api/index/:id');
return {
indexResource:indexResource
}
}]);
commonServices.factory('indexService',['appResources',
function (appResources){
var getAllIndexes =function(sucesscallback){
appResources.indexResource.query({}).$promise.then(
//success
function( value ){/*success events and Data is present in the 'value'*/
sucesscallback(value);
},
//error
function( error ){/*failure events*/}
)
}
}
]);
});
Controller Method calling the service and getting the value in $scope variables like
(function () {
'use strict';
angular.module('saniehhaApp')
.controller('indexController', indexController);
indexController.$inject = ['$location', 'indexService'];
function indexController($location, index,indexService) {
/* jshint validthis:true */
indexService.getAllIndexes(function(sucesscallback){
$scope.value=sucesscallback;
})
//$locaton.qwets=....
}
})();

manage factory JSON $http data with 2 controllers

I'm trying to get a factory JSON response, save it in a variable, in order to be ready to be called from 2 different controllers.
Here bellow I paste the code I'm using:
storyFactory.js
var story = angular.module('story.services', []);
story.factory('storyAudio', [ '$http', function ($http) {
var json = {};
function getJSON(story_id, callback) {
$http({
url: 'https://api.domain.co/get/' + story_id,
method: "GET"
}).success(function (data) {
json = data;
callback(data);
});
};
return {
getSubaudios: function(story_id, callback) {
getJSON(story_id, function(result) {
callback(result);
});
},
getTopbar: function(callback) {
callback(json);
}
};
}]);
StoryCtrl.js
var storyCtrl = angular.module('story', ['story.services']);
storyCtrl.controller('storyCtrl', [ 'CONFIG', '$stateParams', 'storyAudio', function(CONFIG, $stateParams, storyAudio) {
var data = this;
data.story = {};
storyAudio.getSubvideos($stateParams.story_id, function(response) {
data.story = response;
});
}]);
TopbarCtrl.js
var topbarCtrl = angular.module('topbar', ['story.services']);
topbarCtrl.controller('topbarCtrl', [ 'CONFIG', '$stateParams', 'storyAudio', function(CONFIG, $stateParams, storyAudio) {
var data2 = this;
data2.story = {};
storyAudio.getTopbar(function(response) {
data2.story = response;
});
}]);
The problem is in my TopbarCtrl response I'm receiving an empty data2.story when I call it in the HTML.
The reason is because it doesn't have a callback of the $http response, so it prints the var json with the actual status, that is an empty object.
How could I load the second controller when the variable has content?
Thanks in advice.
I think the best you can do in this case is load the data via getSubaudios and provide a reference to the data for other controllers to use. Something like this...
story.factory('storyAudio', function($http) {
var factory = {
story: {}
};
factory.getSubaudios = function(story_id) {
return $http.get('https://api.domain.co/get/' + story_id).then(function(response) {
return angular.extend(factory.story, response.data);
});
};
return factory;
})
Using angular.extend() instead of directly assigning a value to the factory's story property maintains any references that may be established before the data is loaded.
Then you can load the data via
storyCtrl.controller('storyCtrl', function(storyAudio) {
var data = this;
storyAudio.getSubaudios($stateParams.story_id).then(function(story) {
data.story = story;
});
})
and directly reference the story data by reference in your controller
topbarCtrl.controller('topbarCtrl', function(storyAudio) {
this.story = storyAudio.story;
})
I think I'm understanding correctly, but let me know if not.
There are two issues I'm seeing. The first is that there is a typo in your StoryCtrl.js file. You are calling "storyAudio.getSubvideos" but the function is called "getSubaudios" in your factory.
Even with that typo fixed, the issue could still technically happen. It all really depends on how quickly the promise returns from the first call. Unfortunately, promises are asynchronous, so there is no guarantee that the "json" variable will get set before the second controller tries to get it.
In order to resolve this, you need to ensure that the first call is finished before trying to access the "json" variable you have on the service. There are probably a few different ways to do this, but one that comes to mind is to actually return and store the promise in the service like so...
var dataPromise;
function getSubaudios(story_id){
if(!dataPromise){
dataPromise = $http({
url: 'https://api.domain.co/get/' + story_id,
method: "GET"
});
}
return dataPromise;
}
return {
getSubaudios: getSubAudios
};
Then in your controllers, you can just call the service and use .then to get the data out of the promise when it returns...
storyAudio.getSubaudios($stateParams.story_id).then(function(response){
data.story = response; //or data2.story = response;
});
Here is a plunkr example. I've used the $q library to simulate a promise being returned from an $http request, but it should illustrate the idea.
Similar to Phil's answer. (Angular extend, or angular copy keeps the references the same in both controllers. If you don't want to put watchers in both controllers to keep track if the value changes.) Several methods here:
Share data between AngularJS controllers.
You could also bind the object you are returning directly to the update-function. That way the references stay intact.
storyServices.factory('storyAudio', ['$http', function($http) {
return {
data: { json: '' },
getSubaudios: function(story_id) {
$http.get('http://jsonplaceholder.typicode.com/posts/' + story_id)
.then(function(response) {
this.data.json = response.data.body;
}.bind(this));
}
};
}]);
var storyCtrl = angular.module('story').controller('storyCtrl', ['$scope', 'storyAudio', function($scope, storyAudio) {
$scope.data = storyAudio.data;
storyAudio.getSubaudios(2);
}]);
var topbarCtrl = angular.module('story').controller('topbarCtrl', ['$scope', 'storyAudio', function($scope, storyAudio) {
$scope.data2 = storyAudio.data;
}]);
Plunk here: http://plnkr.co/edit/auTd6bmPBRCVwI3IwKQJ?p=preview
I added some scopes to show what happens.
Sidenote:
I think it's straight out dishonest to name your non-controller "storyCtrl" and then assign it a controller of its own:
var storyCtrl = angular.module(...); // Nooo, this is not a controller.
storyCtrl.controller(...); // This is a controller! Aaaah!
Another sidenote:
.success() is the old way of doing things. Change to .then(successCallback) today! I dare to say it's the standard convention for promises.
https://docs.angularjs.org/api/ng/service/$http#deprecation-notice

Angular Promise not working

I try to get some important things like: companyid,employeeid etc. with every request that a user makes. So this has to be received before everything else is done.
After that the user receives information based on his companyid that he sets with every request (get/company/{companyid}).
The problem that I have is that the response for receiving the companyid takes to long and angular already tries to make a request to (get/company/{companyid}) obviously there is no companyid yet.
I've tried to fix this whit promise but it's not working.
Here I try to receive some important information about the user(that I do with every request) :
Service
(function () {
angular.module('employeeApp')
.service('authenticationservice', authenticationservice);
function authenticationservice($http,$location,authenticationFactory,$q,GLOBALS,$cookies) {
this.validateUser = function () {
var vm = this;
vm.deferred = $q.defer();
data = {"api_token": api_token};
return $http.post(GLOBALS.url+'show/employee/' + $cookies.get('employeeid'),data)
.success(function(response)
{
vm.deferred.resolve(response);
})
.error(function(err,response)
{
vm.deferred.reject(err);
});
return vm.deferred.promise;
}
}
})();
Routes file
(In my routes file I use the authenticationservice to set all important users variables.)
employeeAppModule.run([
'authenticationservice',
'constants',
function(authenticationservice,constants) {
authenticationservice.validateUser()
.then(function(response)
{
constants.companyid = response.result.Employee;
constants.role = response.result.Role;
constants.name = response.result.FirstName;
console.log('test');
},
function(response){
console.log('error');
});
}
]);
So the problem is that the user information is set to late and angular already goes to my homeController where he uses the companyId that is not being set yet.
Thankyou
The problem in your current code is return $http.post are having two return statement in your validateUser method. Which is returning $http.get before returning return vm.deferred.promise; & that why customly created promise doesn't get returned from your method. Though by removing first return from $http.get will fix your problem, I'd not suggest to go for such fix, because it is considered as bad pattern to implement.
Rather I'd say, you should utilize promise return by $http method, & use .then to return data to chain promise mechanism.
Code
function authenticationservice($http, $location, authenticationFactory, $q, GLOBALS, $cookies) {
this.validateUser = function() {
var vm = this;
data = {
"api_token": api_token
};
return $http.post(GLOBALS.url + 'show/employee/' + $cookies.get('employeeid'), data)
.then(function(response) {
var data = response.data;
retrun data;
}, function(err) {
return $q.reject(err);
});
}
}
To make sure that $ http return a $ promise object you need to check that the action in the controller returns a value and it is not a void action.

Angular - ngResource breaks data binding

I am new to Angular, and am trying to get up to speed with ngResource.
I created a factory in my chapter.service.js file
angular.module('myApp')
.factory('Chapter', function ($resource) {
return $resource('/api/book/chapter/:id'); // Note the full endpoint address
});
matchescontroller.js
angular.module('myApp').controller('matchesCtrl', function($scope, $location, Chapter) {
// This is used to get URL parameters
$scope.url = $location.path();
$scope.paths = $scope.url.split('/');
$scope.id = $scope.paths[2];
$scope.action = $scope.paths[3];
//Trying to call the test data
var chapters = Chapter.query();
$scope.myFunction = function() {
alert(chapters.length);
}
My view where I test the function
<button ng-click="myFunction()">Click Here</button>
I created a test function to test whether my query returned any results. When I click on the button, I'm alerted with 0, which means the query didn't work.
When I change the function to
$scope.myFunction = function() {
console.log(Object.keys(chapters));
}
I get [$promise, $resolve], but none of the Schema keys
I must be doing something wrong, but I was looking at this tutorial
http://www.masnun.com/2013/08/28/rest-access-in-angularjs-using-ngresource.html
Any help will be appreciated.
Edit: Here is the response I got from the server
GET http://localhost:9000/api/book/chapter/1 500 (Internal Server Error)
$scope.myFunction = function() {
Chapter.query({}, function(data) {
$scope.chapters = data;
}, function(error) {
// custom error code
});
}
When working with $resource I prefer to use the success/error handlers that the API comes with as opposed to dealing the promise directly. The important thing to realize is that just because you called query does not mean that the result is immediately available. Thus the use of a callback that handles success/error depending on what your backend returns. Only then can you bind and update the reuslt in the UI.
Also, while we're talking about it I notice that you didn't wire up the optional paramter in your $resouce URL. $resource takes a second paramter which is an object that supplies mapping for the /:id part of your route.
return $resource('/api/book/chapter/:id', {id: '#id'});
What this notation means is that you pass an object to $resource that has a property called id, it will be subbed into your URL.
So this:
$scope.item = {id: 42, someProp: "something"};
Chapter.get({$scope.item}....
Will result in an API call that looks like '/api/book/chapter/42'
You get a promise from $resource and not the "result" from your database. The result is inside the promise. So try this
var chapters = Chapter.query();
$scope.myFunction = function() {
chapters.then(function(data) {
console.log(data);
});
}
I must admit, that I am not thaaaaat familiar with ngResource, so Jessie Carters is right, the correct syntax is:
chapters.get({...}, function callback() {...})

The variable scope/nesting of AngularJS variables

I have this angular controller:
app.controller('FeedCtrl', function ($scope, Profile) {
$scope.getID = function() {
Profile.getUID()
.then(function (data) {
if (data !== null) {
console.log(data.id); // returns correct id
$scope.data = data;
} else {
console.log("Could not retrieve id");
}
}, function (error) {
console.log(error);
});
console.log($scope.data); // logs: undefined
return $scope.data; // logs: undefined
};
var somedata = $scope.getID();
console.log(somedata); //just returns undefined
});
And this Factory that the controller uses for a JSON request.
module.factory('Profile', function($http, $localStorage) {
return {
getUID:function(){
return $http.get("https://graph.facebook.com/v2.2/me", {params: { access_token: $localStorage.accessToken, fields: "id,name,gender,location,website,picture,relationship_status", format: "json" }})
.then(function(response) {
if (typeof response.data === 'object') {
return response.data;
} else {
// invalid response
return $q.reject(response.data);
}
}, function(response) {
// something went wrong
return $q.reject(response.data);
});
}
};
});
The Question
I am unable to change the value of $scope.data for use outside the $scope.getID function but inside the rest of the FeedCtrl.
If you look on the comments you see what I am getting returned in the console logs.
I've tried to understand this problem by searching here in StackOverflow and Google but it seems that I don't understand the $scope concept of AngularJS.
I am grateful for any push in the right direction.
That's a classic mistake, the code you're calling is asynchronous, look at your console and watch the order of your logs, the log that will return the correct id will be the last one because it will be called only after the promise has been resolved.
Is this enough of a push in the right direction for you?
A very simple example, but it's the same principle.
setTimeout(function(){
document.write('A2: Timeout is done');
}, 5000);
document.write('A1: Called timeout, this gets logged before A2 even though my line number is higher.');
That is not a scope problem, it's a time problem. You are trying to use the value before it exists.
In the controller you can only use the value after the result has arrived, i.e. in the callback for the then method. The return statement runs before there is a result, so you can't return it.
Once you have made an asynchronous call, the result has to be handled asynchronously. You can return a Future object to handle the result when it arrives, but you can never make a function that makes an asynchronous call and returns the result itself.
Your code is asynchronous. I wrote a response to this exact same problem in this post.
Returning after ajax call prints false

Categories

Resources