AngularJS AppCtrl wait for HTTP event to success - angularjs

I'm all new to AngularJS and need some help, I have a "AppCtrl" and from there I have a HTTP webservice call - and need the webservice call response accessible in my other controllers.
angular.module('starter.controllers', [])
.controller('AppCtrl', function($scope, $http) {
$scope.webservice_url = "http://webserviceurl.com/";
$http.get($scope.webservice_url+"?action=get_settings").success(function(data, status, headers, config) {
$scope.stations = data.stations;
});
})
This works FINE - and i can access the $scope.stations in my templates - BUT now i want to access the $scope.stations in my "PlaylistCtrl" controller, but this is undefined :(
.controller('PlaylistCtrl', function($scope, $stateParams) {
console.log($scope.stations); // is undefined :(
})
How can I make sure the http call is "done" (success) before the "PlaylistCtrl" is loaded ...

you should turn the http into service/factory if possible
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope, dataService) {
dataService.then(function (data) {
$scope.data = data
})
});
app.controller('SecondCtrl', function($scope, dataService) {
dataService.then(function (data) {
$scope.secData = data
})
});
app.service('dataService', function ($http, $q){
var defferer = $q.defer()
$http.jsonp('http://ip.jsontest.com/?callback=JSON_CALLBACK').success(function (data){
defferer.resolve(data)
})
return defferer.promise
})
http://plnkr.co/edit/8k97DngZ8KoFOBPFJG82?p=preview working example

$scope.stations ist not undefined in PlayListCtrl because the HTTP call has not finished, but because PlayListCtrl has a different $scope than AppCtrl.
You should put the webservice call into a service and inject that service into all controllers that require it.

In Angular, you don't wait for data before rendering. You let it render, even if the array is empty initially, and then once the data returns, you render again. Angular does this to maintain a high level of responsiveness. It is by design, and not something you should try to change.
All you need to do to fix your undefined variable is initialize the stations scope variable in your controller. Although it gets overwritten when the data returns from your service, it doesn't matter because angular will watch for changes to the scope variable by reference and update all views when it does.
angular.module('starter.controllers', [])
.controller('AppCtrl', function($scope, $http) {
$scope.webservice_url = "http://webserviceurl.com/";
$scope.stations = [];
$http.get($scope.webservice_url+"?action=get_settings").success(function(data, status, headers, config) {
$scope.stations = data.stations;
});
})
In your inner controller, if the data hasn't returned yet, $scope.stations will be an empty array:
.controller('PlaylistCtrl', function($scope, $stateParams) {
console.log($scope.stations); // is []
$scope.$watch('stations', function(newVal) {
alert('data returned!! updating views!');
});
})
Once the data returns, the array reference on the scope is overwritten, any $watch handlers are called to update the view.

// Make a remote request.
$http.get('some wonderful URL for a service').success(function (results) {
superImportantInfo = results;
semaphore = true;
});
while (!semaphore) {
// We're just waiting.
}
This is how you can let your controller to wait till it finishes execution of controller 1 and before moving to the next controller.
Hope this help!

Related

AngularJS factory return no data first time, but after called interval method it does

Im trying to make a AngularJS factory that provides my app with a list of businesses. But i cant seem to get the variable the first time this is run. But after interval is run, i get the variable.
I get this on the first run on the controller in the page:
angular.js:12783 Error: [$injector:undef] Provider 'businessList' must return a value from $get factory method
But I think my solution is faulty, any how? Can anyone point me in the right direction here? For example is using rootScope here a good idea?
What I want is a globally accessible list of businesses in my app, that is collected on start of visit, and updates itself with a timer. So i dont have to all the time call for induvidial requests from the laravel backend, when i can just find it in that list.. is my idea.
Factory:
myApp.factory('businessList', ['$interval', '$http', '$rootScope',
function($interval, $http, $rootScope) {
function bedriftliste() {
$http.get('get/allebedrifter')
.then(function(result) {
bedrifter = result.data;
$rootScope.bedrifter = bedrifter;
});
return $rootScope.bedrifter;
}
var bedrifter = bedriftliste();
// start periodic checking
$interval(bedriftliste, 5000);
return bedrifter;
}
]);
Controller
myApp.controller('bsC', ['$rootScope', '$scope', 'businessList',
function($rootScope, $scope, businessList) {
$scope.allebedrifter = businessList;
}]);`
I solved this by just doing a http.get if object was null.
if (!$rootScope.allebedrifter) {
$http.get('get/bedrift/' + $scope.tslug)
.then(function(result) {
bedriften = result.data;
$scope.bedriften = bedriften;
});
Seems to work fine like this
Although I am late in pointing out but that doesn't seem to be a proper solution to this problem. You need to make these changes in factory:
myApp.factory('businessList', ['$interval', '$http', '$rootScope',
function($interval, $http, $rootScope) {
function bedriftliste() {
return $http.get('get/allebedrifter');
}
}
]);
and in the controller you'll do something like this:
myApp.controller('bsC', ['$rootScope', '$scope', 'businessList', function($rootScope, $scope, businessList) {
function TestFunction(){
businessList.bedriftliste().then(function successCallback(response) {
$scope.allebedrifter = response.data;
//it will invoke 5 seconds after you receive the response from your factory method, I didn't test it but it will solve your problem
$interval(TestFunction, 5000);
}, function errorCallback(response) {
});
}
}]);

Angularjs cannot get data from service

I'm trying to pass data from one controller to another using a service, however no matter what I'm trying it always returns 'undefined' on the second controller. Here is my service :
app.service('myService', ['$rootScope', '$http', function ($rootScope, $http) {
var savedData = {}
this.setData = function (data) {
savedData = data;
console.log('Data saved !', savedData);
}
this.getData = function get() {
console.log('Data used !', savedData);
return this.savedData;
}
}]);
Here is controller1 :
.controller('HomeCtrl', ['$scope','$location','$firebaseSimpleLogin','myService','$cookies','$window', function($scope,$location, $firebaseSimpleLogin, myService, $cookies, $window) {
loginObj.$login('password', {
email: username,
password: password
})
.then(function(user) {
// Success callback
console.log('Authentication successful');
myService.setData(user);
console.log('myservice:', myService.getData()); // works fine
}]);
And then controller2:
// Dashboard controller
.controller('DashboardCtrl', ['$scope','$firebaseSimpleLogin','myService',function($scope,$firebaseSimpleLogin, $location, myService) {
console.log('myservice:', myService.getData()); //returns undefined
}]);
That is simple code, unfortunately I've been struggling for a few hours now, any suggestion ? Thanks.
Created a fiddle here:
http://jsfiddle.net/frishi/8yn3nhfw/16
To isolate the problem, can you remove the dependencies from the definition for myService and see if that makes it work? Look at the console after you load the fiddle.
var app = angular.module('app', [])
.service('myService', function(){
this.getData = function(){
return "got Data";
}
})
I assume the issue is that you are returning this.savedData in the service. Try returning savedData.
this behaves different in Javascript than in other languages.

How get access to global variable from Factory Angular JS?

I tried to write factory method in Angular JS:
.factory('FriendsFactory', function(){
var friend = {};
friend.delete = function(id) {
notificationsModal.show('danger', messages_info[86]);
notificationsModal.confirm(function() {
this.deleteAjax(event, id, type);
})
}
friend.deleteAjax = function (event, id, type){
var target = angular.element(event.target);
var request = $http({
method: "POST",
url: "/subscribe/deletesubscriber",
data: $.param({ id : id, type : type }),
headers: {'Content-Type': 'application/x-www-form-urlencoded'}
});
request.success(function () {
target.closest('.notif-item').remove();
$scope.counter--;
});
request.error(function () {
// TODO
});
}
return friend;
})
This code has two methods: friend.delete() also friend.deleteAjax()
Calling functions from Factory:
.controller('FriendsController', ['$scope','$http', 'friendsFactory', function($scope, $http) {
$scope.deleteUser = function (idUser) {
friendsFactory.delete(idUser);
}
}])
I need decrement variable $scope.counter in friend.deleteAjax() ajax response, regardless controller from was called factory.
I can do duplicate in each controller:
$scope.counter = 10; and after call factory, but it is not good
Although the answer suggested by #JBNizet is absolutely correct but if you are bound to use the code in the way it is, then you can do two things. First is to simply pass the $scope from controller to service call (which is not a cleaner approach is not recommended):
$scope.deleteUser = function (idUser) {
friendsFactory.delete(idUser, $scope);
}
And you can use the scope inside the factory.
The second option to use current controller's scope to root scope and then use this in the factory.
In your controller
$scope.deleteUser = function (idUser) {
$rootScope.callingControllerScope = $scope;
friendsFactory.delete(idUser);
}
In your factory
friend.deleteAjax = function (event, id, type){
console.log($rootScope.callingControllerScope.counter);
// your code
}
And you also need to fix your dependency injection:
.controller('FriendsController', ['$scope','$http', 'FriendsFactory', function($scope, $http, friendsFactory) {
$scope.deleteUser = function (idUser) {
friendsFactory.delete(idUser, $scope);
}
}]);
You're doing many, many things wrong:
Using friendsFactoryinstead of FriendsFactory:
.controller('FriendsController', ['$scope','$http', 'friendsFactory'
here --------^
Forgetting to declare friendsFactory as an argument of the controller function:
.controller('FriendsController', ['$scope','$http', 'friendsFactory', function($scope, $http) {
here ----^
Accessing an undefined $scope variable in the service:
$scope.counter--;
^--- here
Doing DOM manipulation in a service...
The service responsibility is not to manipulate the DOM and the controller scope.
The DOM should be modified using directives in the html template.
The controller scope should be managed by the controller, not by the service. Return the promise request from the deleteAjax() function, and let the controller register a success callback, rather than doing it in the service. This callback will then be able to access the controller scope.
Note that most errors are basic JavaScript error that should be signalled by a good JavaScript editor, or at least by looking at errors in the console of your browser.

How to set $scope.property only when corresponding value is defined/resolved

Inside my controller I have this piece of code:
$scope.property = localStorageService.get('property');
To clarify, localStorageService is an angular module which helps deal with setting and getting Local Storage variables.
The problem is localStorageService.get('property') will only be set after a certain period of time because of an asynchronous $http call handled by a factory class. At the time when I do the assignment, localStorageService.get('property') is undefined, but will eventually be resolved by the xhr call.
App.factory('fact', ['$http', 'localStorageService', function($http, localStorageService){
return {
fetchProperty: function() {
$http.get('/property')
.success(function(data, status, headers, config){
localStorageService.set('property', data);
})
}
}
}]);
How do I set $scope.property when and only when localStorageService.get('property') has already been set?
edit
I am using the browser's Local Storage because the retrieved property is meant to persist throughout the application until the user logs out, in which case, it is removed from the browser.
As suggested in the comments, return the promise. You can even chain the then calls like this
return $http.get('/property').then(function(data) {
localStorageService.set('property', data);
return data;
});
Then, in your controller...
fact.fetchProperty().then(function(property) {
$scope.property = property;
});
Have you tried using the bind function of local storage service.
Something like
App.factory('fact', ['$scope', '$http', 'localStorageService', function($scope, $http, localStorageService){
return {
fetchProperty: function() {
$http.get('/property')
.success(function(data, status, headers, config){
localStorageService.set('property', data);
localStorageService.bind($scope, 'property');
})
}
}
}]);
Then you shouldn't need to set the value in your controller.

Problems using $http inside a Service

I have a basic data Service which will be used across Controllers. But I'm having an issue grabbing some data that's been added via $http.
Service:
angular.module('core').service('FormService', ['$http', function($http) {
var _this = this;
_this.dropdownData = {
contactTimes: ['Anytime','Morning','Afternoon','Evening'],
industries: {},
};
$http.get('/json').success(function(resp){
_this.dropdownData.industries = resp.industries;
});
}]);
Controller:
angular.module('core').controller('SignupController', ['$scope', '$http', '$state', 'FormService', function($scope, $http, $state, FormService) {
console.log(FormService.dropdownData); // Shows full object incl industries
console.log(FormService.dropdownData.industries); // empty object {}
}]);
How do I get FormService.dropdownData.industries in my controller?
Create a service like below
appService.factory('Service', function ($http) {
return {
getIndustries: function () {
return $http.get('/json').then(function (response) {
return response.data;
});
}
}
});
Call in controller
appCtrl.controller('personalMsgCtrl', ['$scope', 'Service', function ($scope, Service) {
$scope.Industries = Service.getIndustries();
}]);
Hope this will help
Add a method to your service and use $Http.get inside that like below
_this.getindustries = function (callback) {
return $http.get('/json').success(function(resp){
_this.dropdownData.industries = resp.industries;
callback(_this.dropdownData)
});
};
In your controller need to access it like below.
angular.module('core').controller('myController', ['$scope', 'FormService', function ($scope, FormService) {
FormService.getDropdownData(function (dropdownData) {
console.log(dropdownData); // Shows full object incl industries
console.log(dropdownData.industries); // object {}
});
} ]);
Given that your console log shows the correct object, that shows your service is functioning properly. Only one small mistake you have made here. You need to access the data attributes in your return promise.
angular.module('core').service('FormService', ['$http', function($http) {
var _this = this;
_this.dropdownData = {
contactTimes: ['Anytime','Morning','Afternoon','Evening'],
industries: {},
};
$http.get('/json').success(function(resp){
//note that this is resp.data.industries, NOT resp.industries
_this.dropdownData.industries = resp.data.industries;
});
}]);
Assuming that you're data is indeed existing and there are no problems with the server, there are quite a few possible solutions
Returning a promise
angular.module('core').service('FormService', ['$http', function($http) {
var _this = this;
_this.dropdownData = {
contactTimes: ['Anytime','Morning','Afternoon','Evening'],
industries: {},
};
_this.dropdownData.industries = $http.get('/json');
}]);
//Controller
FormService.industries
.then(function(res){
$scope.industries = res.industries
});
Resolving with routeProvider / ui-route
See: $http request before AngularJS app initialises?
You could also write a function to initialize the service when the application starts running. At the end of the day, it is about waiting for the data to be loaded by using a promise. If you never heard about promises before, inform yourself first.
The industries object will be populated at a later point in time when the $http call returns. In the meantime you can still bind to the reference in your view because you've preserved the reference using angular.copy. When the $http call returns, the view will automatically be updated.
It is also a good idea to allow users of your service to handle the event when the $http call returns. You can do this by saving the $promise object as a property of industries:
angular.module('core').service('FormService', ['$http', function($http) {
var _this = this;
_this.dropdownData = {
contactTimes: ['Anytime','Morning','Afternoon','Evening'],
industries: {},
};
_this.dropdownData.industries.$promise = $http.get('/json').then(function(resp){
// when the ansyc call returns, populate the object,
// but preserve the reference
angular.copy( resp.data.industries, _this.dropdownData.industries);
return _this.dropdownData.industries;
});
}]);
Controller
app.controller('ctrl', function($scope, FormService){
// you can bind this to the view, even though the $http call has not returned yet
// the view will update automatically since the reference was preserved
$scope.dropdownData = FormService.dropdownData;
// alternatively, you can hook into the $http call back through the $promise
FormService.dropdownData.industries.$promise.success(function(industries) {
console.log(industries);
});
});

Resources