How to make data available in all scopes in Angularjs - javascript

I wish to fetch data from the server-side using $http and and make it available to the all routes and controllers in my app.
Javascript code sample
myApp.factory('menuService', function($http){
$http.get('/pages').success(function(data){
return data; //list of pages
});
});
myApp.run(function($rootScope, menuService){
$rootScope.menu = menuService;
});
HTML code sample
<ul >
<li ng-repeat="page in menu">{{page.title}}</li>
</ul>
This code actually returns the data but does not print it on my html page. Please can someone help? Thanks

You are inverting the differed promise pattern.
Instead your code should be:
myApp.factory('menuService', function($http){
return $http.get('/pages');
});
myApp.run(function($rootScope, menuService){
menuService.then(function(data) {
$rootScope.menu = data;
})
});

You may be able to benefit from setting up your menuService a bit different. Try the following...
myApp.factory('menuService', function($http) {
function getData() {
return $http.get('/pages');
}
return {
'getData' : getData
}
});
Now we have a function wrapped in our $http call in a getData() function, we can now easily leverage then() to resolve the promise returned by getData() in .run(), ensuring we get a resolved value and $rootScope.menu gets assigned the values we want. This new setup on menuService now sets the landscape to add other functions at later times, which we'll likely want.
myApp.run(function($rootScope, menuService) {
menuService.getData().then(function(response) {
$rootScope.menu = response;
})
});
Check out the $http docs for a better understanding on asynchronous behavior

Related

Angularjs Scope Variable does not Update View

I have seen this issue a lot on SO, but none of the solutions I have tried are working for me. I tried using $apply() which gives me an error stating that the digest cycle is already running. I tried using the Dot "." notation, but nothing changes. I even tried using timeout and promises, but still it does not update in the view.
Here is the HTML:
<th colspan="4" ng-class="{'ssqtrue': deficiencies === false , 'ssqfalse': deficiencies === true}">
<span ng-model="definfo">{{definfo}}</span>
</th>
Here is my Controller code:
$scope.recalculateDashboard = function (goToDashboard) {
contractorService
.calculateScores()
.success(function () {
getscoringDetails();
getDefInfo();
if (goToDashboard) {
$scope.tabs[0].active = true;
}
}).error(function (reason) {
console && console.log(reason.statusText);
genericErrorAlertHandler();
});
};
function getDefInfo() {
contractorService.getDeficiencyInfo()
.success(function (data) {
$scope.$apply(function() {
$scope.definfo = data;
});
if ($scope.definfo == 'No Deficiencies Found') {
$scope.deficiencies = false;
} else {
$scope.deficiencies = true;
}
}).error(function (reason) {
console && console.log(reason.statusText);
genericErrorAlertHandler();
});
}
For the life of me, I can't figure out what is going on here. Any assistance is greatly appreciated!
Regards to <span ng-model="definfo">{{definfo}}</span>
You don't need ng-model directive here. Enough to use {{definfo}} or better way like folks pointed to use ng-bind like:
<span ng-bind="definfo"></span>
I tried using $apply() which gives me an error
Generally, developers use $scope.$apply when get callback from 3d party code like jQuery that stay out of digest cycle. So don't use it. BTW, safety way is to use $timeout for example a.e. wrap as:
$timeout(function () {
$scope.definfo = data;
});
I believe if you don't use Promise chain you can use success and error callbacks (what you actually did) instead to use then()
About $http.get('/SSQV4/SSQV5/Contractor/GetDeficiencyInfo');
$http.get returns original Promise but it also returns status 'Ok', config, headers ... and i'm sure you don't want parse full response in your controller.
So I would create new Promise in your service and fetch results only.
So instead: return $http.get(URL); I would write in Service:
this.getDeficiencyInfo = function () {
var deferred = $q.defer();
$http({method: 'GET', url: URL}).then(function(res){
deferred.resolve(res.data);
}, function (error) {
console.error(error);
deferred.resolve({error:error}); //actually you can reject
});
return deferred.promise;
};
DEMO in fiddle
I figured this out, although I don't know "why" it caused this particular issue. When the page first loads $scope.definfo is set by a variable from another http call. The variable var pagedata = [] holds a lot of information that comes back from the service when the page is first rendered. The scope variable is then set with this code: $scope.definfo = pagedata.DeficiencyInfo. Removing that line and calling the function getDefInfo directly, sets and updates the scope variable as desired. Perhaps one of you gurus could explain why that is to me and others so that it may help others. Thank you to everyone for your help.

Angular 1 promise in service

I've been going crazy trying to get something to work in Angular 1.3 that may not even be possible... but it seems like it should be. Basically, I'd like to write a service that returns only a calculated value, not a promise.
I have a service that makes an $http.get request and returns a promise. I'd like to call this from another service, get that data, manipulate it and return a value. I don't want to have to deal with .then once I've called my service. My reasoning is that I'd like to have a sharable function that I can call and receive an actual value so that I can use it as a condition for something like ng-show. Like I say, I'd write this function in the controller and use .then to assign a variable with the data from the promise, but I don't want to write that in every single controller I need this in.
Example:
app.factory('searchService', ['$http', function($http) {
return {
performSearch: function(searchString) {
return $http.get('http://asdf.com/search/' + searchString);
},
getItemCount: function(itemName) {
var _this = this;
this.count = 0;
this.performSearch(itemName).then(
function successCallback(resp) {
_this.count = resp.data.items.length;
},
function errorCallback() {
// This section doesn't matter right now
};
return this.count;
}
};
}]);
So I'd like to just be able to call this from the controller and get back a "5" or something. Or even better, calling it from an ng-show like "ng-show="blahCtrl.getItemCount('item_search_string') == 0". Again, I have multiple controllers I may need this in, so I'd rather not duplicate functions all over the place just to extract that number I need.
This just doesn't work for me though... my return value is always whatever I initially set this.count to be. I've inserted a console.log a few places and have seen that (it seems) this.count is returned before the callback function can set its value. No matter what I do, I'm incapable of just returning a single value from this service and it's driving me crazy! What confuses me more is that if I were to do this code from inside the controller:
var _this = this;
searchService.performSearch('asdf').then(
function(data) { _this.searchResults.data; }
);
It works just fine and I get my promise data right inside of the searchResults variable no problem. It just doesn't work that way inside of the service for some reason.
Hopefully this makes sense, I may have rambled a bit or been unclear. Any help would be greatly appreciated... I've spent about as much time researching this and using trial and error on my own as I can.
Thanks!
One solution as i see.
Put this once in controller
app.controller('ctrl', function ($scope, searchService) {
$scope.searchService = searchService;
searchService.getItemCount();
});
and get in all view
{{ searchService.count }}
And i think, it's bad to make a request for each ng-if, ng-show etc.
You are going to need to use $q (https://docs.angularjs.org/api/ng/service/$q). Your function is returning before the promise is fulfilled. Your getItemCount should look like this:
app.factory('searchService', ['$http', '$q', function($http, $q) {
return {
performSearch: function(searchString) {
return $http.get('http://asdf.com/search/' + searchString);
},
getItemCount: function(itemName) {
var deferred = $q.defer();
this.performSearch(itemName).then(
function successCallback(resp) {
deferred.resolve(resp.data.items.length);
},
function errorCallback(err) {
deferred.reject(err);
};
return deferred.promise;
}
};
}]);

Able to access object returned by service, but accessing its properties gives undefined

I'm an angular beginner and I'm trying to set up a factory that gets data from my php back-end and updates the controller. Here is my basic code:
factory
app.factory('sessionStatus', ['$http', function($http){
return {
status:function(){
var status = {};
$http.get('/game_on/api/api.php?session-status')
.then(function(response){
angular.copy(response.data, status);
});
return status;
}
};
}]);
controller
app.controller('headerCtrl', ['$scope', 'sessionStatus', function($scope, sessionStatus){
$scope.sessionStatus = sessionStatus.status();
console.log($scope.sessionStatus);
}]);
This gives me the following in the console:
Object{}
session:"false"
However, when I adjust the controller to log the value of the session property like so:
console.log($scope.sessionStatus.session);
I simply get undefined in the console.
What am I doing wrong?
EDIT
For anyone else who suffers from my distinct brand of retardation, here's the conclusion I've reached:
The main cause of the issue is that I've been trying to access asynchronously generated information returned by the $http service before its promise has been resolved (i.e. before .then() is finished executing), which is leading to undefined being logged because the data has literally not yet been defined since the request that generates it is still in progress by the time console.log() is called in the controller.
Using the $timeout service I was able to delay the calling of console.log(), which resulted in the data being correctly logged to the console.
$timeout(function(){
console.log($scope.sessionStatus.session);
}, 2000); // logs "false" to the console like originally expected
My previous thinking was that I should make the call to my factory method as short as possible within the controller, and then act upon its results outside of that call. This was obviously a bad idea because .then() exists so that you can act on the results of an asynchronous request at the precise moment the request is completed.
In hindsight this all seems so obvious, but hopefully this explanation helps someone else in the future...
Thanks for all the answers I received, they were a big help!
There are a couple of changes that you can make to the code here. First I would suggest resolving the promise and assigning the value in the controller instead of in the service. Next, you won't need to use Angular.Copy on the result object, you can simply make a variable assignment. Try something like simply this, then build out:
app.factory('sessionStatus', ['$http', function($http){
return {status: $http.get('/game_on/api/api.php?session-status')}
}]);
and
app.controller('headerCtrl', ['$scope', 'sessionStatus', function($scope, sessionStatus){
sessionStatus.status.then(function(result){
$scope.sessionStatus = result.data; //or some property on result
});
}]);
since javascript is asynchronous it does't wait until http response and return the status. thats why it return null. try something like this
app.factory('sessionStatus', ['$http', function($http){
return {
status:function(){
$http.get('/game_on/api/api.php?session-status')
}
};
}]);
app.controller('headerCtrl', ['$scope', 'sessionStatus', function($scope, sessionStatus){
$scope.sessionStatus = sessionStatus.status().then(function(response) {
console.log(response);
});
}]);
Try to return a promise from your factory:
app.factory('sessionStatus', ['$http', function($http) {
var factory = {
status: status
};
return factory;
function status() {
return $http.get('/game_on/api/api.php?session-status');
}
}]);
Then in controller:
app.controller('headerCtrl', ['$scope', 'sessionStatus', function($scope, sessionStatus) {
function getSuccess(response) {
$scope.sessionStstus = response.data;
}
function getError(response) {
console.log('error');
}
sessionStatus.status()
.then(getSuccess)
.catch(getError);
console.log($scope.sessionStatus);
}]);

AngularJS Service - Make $resource return data right away instead of a promise so clients don't have to use .then()

This is a simple problem but seems tricky due to asynchronous nature of promises.
With in my data service, I want to ensure that data is retrieved back from the server before moving on to next step as other functions/clients depend on that data.
I don't want clients to register callbacks or use .then() and certainly not use $timeout when requesting the data.
How can I make sure that the controller and service that depend on my Data service get the data right away when requested? I've explained my problem using the code below. I would very much like to continue on my current approach if possible.
AngularJS Version: 1.4+
My Current Approach
//Data Service
App.factory('HealthyFoodService', function($resource, $q) {
var fruitsPromise = $resource('api/food/fruits', {}).query().$promise;
var veggiesPromise = $resource('api/food/veggies',{}).query().$promise;
var fruitsData, veggiesData;
//Ensure $q populates the data before moving on to next statement.
$q.all([fruitsPromise, veggiesPromise]).then(function(data) {
fruitsData = data[0];
veggiesData = data[1];
}
function getCitrusFruits() {
var citrusFruits;
var allFrutis = fruitsData;
//code breaks here because fruitsData is still undefined when called from a controller or another service.
//some logic for this function
return citrusFruits;
}
function getLeafyVeggies() {
var leafyVeggies;
var allVeggies = veggiesData;
//code breaks here because veggieData is still undefined when called from a controller or another service.
//some logic for this function
return leafyVeggies;
}
function getLeafyVeggyByName(name) {
//this function is called from other services and controllers.
var leafyVeggies = getLeafyVeggies();
return leafyVeggies[name];
}
return {
getCitrusFruits: getCitrusFrutis,
getLeafyVeggies: getLeafyVeggies,
getLeafyVeggyByName: getLeafyVeggyByName
});
Below are the two clients. One is a controller and another is a service. They both need the data right away as following statements depend on the returned data.
//Controller
App.controller('LeafyVeggieController', function(HealthyFoodService) {
//Ideally I just'like to do something like below instead of calling `.then()` and registering callbacks.
var leafyVeggies = FoodService.getLeafyVeggies();
//leafyVeggies is undefined because data is not available yet;
});
//Another service depending on HealthyFoodService- similar scenario
App.factory('LeafyVeggieReportService', function(HealthyFoodService) {
function generateLeafyVeggieReport() {
var name = 'spinach';
var veggieInfo = HealthyFoodService.getLeafyVeggieByName(spinach);
//veggieInfo is undefined
//logic that need data.
});
My Previous Approach
Below is how I had it partially working before but I wasn't happy about using .then() everytime I needed the data.(Even with in the same service)
App.factory('HealthyFoodService', function($resource, $q) {
//resource variables;
function getLeafyVeggies() {
return $q.all([veggiesPromise]).then(function(data) {
//logic
return leafyVeggies;
});
}
function getLeafyVeggieByName() {
var leafyVeggies = getLeafyVeggies().then(function(data) {
return data;
}
//some logic
//still causes issues when called from another service because above call doesn't get the data right away.
}
return {
getLeafyVeggies: getLeafyVeggies,
getLeafyVeggieByName: getLeafyVeggieByName
}
//controller
App.controller('LeafyVeggieController', function(HealthyFoodService) {
var leafyVeggies = HealthyFoodService.getLeafyVeggies().then(function(data) {
return data;
});
//controller related logic
});
Update
I'm using ui-router as well, so I'm aware that I can use resolve:{} in $stateProvider to inject the data directly into the controller. The puzzle is how to get the data when I make a request from another service or from another function with in the same service without having to use .then().
Solution
Using $q.all([]) in my client services that depend on my Data service has done the trick for me. I have used $q.all([]) whenever I'm in a situation where I need all the data to be present before start processing the logic.
I still have to use .then() on my clients, but by using $q.all([]), I can still slightly simulate a synchronous flow without breaking any asynchronous principles.
I don't think this is possible. There is inherent latency in network operations that you need to wait for. Not doing so results in the application continuing before the data is available.
That being said, most of the native model binding operations will implicitly wait on promises, so there would be no need to .then if no further manipulation of the data is necessary before passing to the view. You can also use the transformResponse method of ngResource to help with this.
Another option might be to shift the complexity to the resolve methods in the route config. In that case you would handle the .then in the resolve and pass the resolved data to your controller. This will keep your controllers cleaner, but still requires you resolve the promises in the route config.
Try having the service hold your data and have the controller reference that data, instead of trying to pass it to your controller scope. Resolve the promise inside the service like so:
App.factory("HealthyFoodService", function($resource,$q) {
var service = {};
service.data = {
fruitsData: [],
veggiesData: []
}
$resource('api/food/fruits', {}).query().$promise.then(function(data) {
$service.data.fruitsData = data[0];
$service.data.veggiesData = data[1];
})
service.getCitrusFruits = function() {
var citrusFruits;
// Perform some logic on service.data.fruitsData
return citrusFruits;
}
return service;
})
In you controller, you can talk to the service like so:
App.controller("FruitController", function($scope, HealthyFoodService) {
// Now you can access the service data directly via $scope.hfs.fruitsData
$scope.hfs = HealthyFoodService.data;
// Or we can create a local copy of the data using the getCitrusFruits function
$scope.citrusFruits = HealthyFoodService.getCitrusFruits();
})

AngularJS don’t get JSON data

I want to get data from a JSON file by using $http.get with AngularJS.
I use the "new way" to write AngularJS scripts (link).
PLUNKER
Nothing happens, Firebug tells me nothing.
I think the problem is in my activate() function code, I don’t really understood the promises.
function activate() {
return $http.get('test.json').then(function(data) {
vm.result = data;
return vm.result;
});
}
Have you an idea about this ?
I see a couple of problems.
First, in your plunker code you have:
controller.$inject = ["$http"];
function controller() {
You are missing the $http parameter in your controller function signature.
function controller($http) {
Once I fixed that, I found your index.html page was binding to {{c.value}}, but your controller never defines a value property. Maybe this should be {{c.result}}? If I make these changes I get a visible result.
You're not able to return at that point in your then callback. Simply return the $http call itself, of which will be a promise, and resolve it. Also, your console should be telling you something like $http is undefined since you did not inject this service properly. Observe the following...
function activate() {
return $http.get('test.json')
}
[...]
activate().then(function(response) {
vm.result = response.data;
});
Plunker - demo
Side note - you'll likely want to wrap activate() into an reusable service to keep this logic out of our controllers, so we'll instead inject the defined service into controller instead of $http directly.
Plunker - demo with this logic wrapped in a simple service

Categories

Resources