Following is the code that makes an http request to MyApp's API for user profile data(like name, photo) to update the navbar.
var app = angular.module('MyApp', ['ng']);
app.controller('navBarController', function($scope, $userProfile) {
$scope.userProfile = $userProfile;
console.log("$userProfile: " + JSON.stringify($userProfile));
setTimeout(function() {
console.log("$userProfile: " + JSON.stringify($userProfile));
}, 3000);
});
app.factory('$userProfile', function($http) {
var profile = null;
$http.
get('/api/v1/me').
success(function(data) {
profile = data;
console.log("profile after get: " + JSON.stringify(profile));
}).
error(function(data, $status) {
if ($status === status.UNAUTHORISED) {
profile = null;
console.log("profile if error: " + JSON.stringify(profile));
}
});
console.log("profile (finally): " + JSON.stringify(profile));
return profile;
});
app.directive('navBar', function() {
return {
controller: 'navBarController',
templateUrl: 'templates/nav_bar.html'
}
});
I am console logging to check for the unexpected results I am getting and the logs are as follows:
profile (finally): null
$userProfile: null
profile after get:
{"photo":"http://localhost:3000/img/1023.jpg","name":"Utkarsh Gupta"}
$userProfile: null
The first two log msgs are obvious as $http.get() is asynchronous so the profile is null as defined at the start of the function. But after the $http.get() function returns successfully, the profile var got updated as shown in the third log msg but the $userProfile service continues to be null.
Looks like your service is not injected into the controller.
Have you tried it that way?
app.controller('navBarController', ["$scope", "$userProfile", function($scope, $userProfile) {}]
Example here:
https://docs.angularjs.org/guide/services
In your service, you have declared var profile = null; and triggering the $http api call then immediately returning the variable profile which is null at the time of returning and later you are updating the variable once the api got response and you are expecting it should be propagated to the controller which is not the case.
As service is singleton in nature, instance will be created once and
never created/updated.
Hence, your code is not a recommended one to use a service. I have updated the code below where service will return a method called load to call the api which is getting triggered from the controller where $scope can be directly assigned with the response data.
var app = angular.module('MyApp', ['ng']);
app.controller('navBarController', function($scope, $userProfile) {
$userProfile.load().
success(function(data) {
$scope.userProfile = data;
console.log("profile after get: " + JSON.stringify($scope.userProfile));
}).
error(function(data, $status) {
if ($status === status.UNAUTHORISED) {
$scope.userProfile = null;
console.log("profile if error: " + JSON.stringify($scope.userProfile));
}
});
});
app.factory('$userProfile', function($http) {
var getProfile = function() {
return $http.
get('/api/v1/me');
};
//console.log("profile (finally): " + JSON.stringify(profile));
return {
load: getProfile
};
});
app.directive('navBar', function() {
return {
controller: 'navBarController',
templateUrl: 'templates/nav_bar.html'
}
});
Note: Also, please don't use $ prefix to service, variable, controller names as this is reserved to AngularJS and may create
conflicts when you use the same name as AngularJS reservered keywords/services.
You need to first fix your services (factory). It needs to return and object. Right now you are just running async code in your service, no way for your controller to use it. Second once you fix your service (look at the code below) you need to create a function to get the user profile. The user profile function needs to return a promise since you are working with async code. Again look at the code below and I hope it helps.
var app = angular.module('MyApp', ['ng']);
app.controller('navBarController', function($scope, $userProfile) {
$userProfile.get().then(function(response){
$scope.userProfile = response;
});
setTimeout(function() {
console.log("$userProfile: " + JSON.stringify($userProfile));
}, 3000);
});
app.factory('$userProfile', function($http) {
var self = {};
self.get = getProfile;
return self;
function getProfile(){
var profile = null;
return $http.get('/api/v1/me')
.success(function(data) {
return data.data;
})
.error(function(data, $status) {
if ($status === status.UNAUTHORISED)
return profile;
});
}
});
Instead of initialising profile as null at the top, you must initialise it as:
var profile = {};
And then tack on to that empty profile object your API returned data like:
$http.
get('/api/v1/me').
success(function(data) {
//like this
profile.data = data;
})
In your code, when the $userProfile-service function finishes it returns the profile simply as null. And even after the $http.get request is complete your $userProfile has the same null because now the updated profile variable is not accessible anymore as it is out of scope. But if you initialize and return profile as an object, you can access it even after the function has returned because objects are passed by reference in javascript. You can then access the content of the profile object in your controller the way you are alreday doing because now $userProfile is the same exact profile object that you declared above and not a null anymore and any update on that profile object anywhere in your entire code will be reflected wherever that object is being accessed.
Related
I have two AngularJs Controllers called "HomeController" and "VacancyController".
My HomeController have method getallData(). Below I am trying to call Homecontroller's getallData() function through ng-change event of the dropdown.
Please advise how do I call getallData() functiona as my dropdown on-change attribute is wrapped around "VacancyController"?
Below is my code:
HTML
<div ng-controller="VacancyController">
<p>Select a Vacancy:</p>
<select ng-model="selectedVacancy" ng-options="x.name for x in vacancieslist" ng-change=""></select>
<h1>Your selected Vacancy Title is : {{selectedVacancy.name}}</h1>
HomeController
.controller('HomeController', function ($scope, angularSlideOutPanel, $http, $location, $window) {
getallData();
//******=========Get All Teachers=========******
function getallData() {
$http({
method: 'GET',
url: '/Home/GetAllData'
}).then(function successCallback(response) {
// this callback will be called asynchronously
// when the response is available
$scope.ListTeachers = response.data;
}, function errorCallback(response) {
// called asynchronously if an error occurs
// or server returns response with an error status.
$scope.errors = [];
$scope.message = 'Unexpected Error while saving data!!';
console.log($scope.message);
});
};
VacancyController
app.controller('VacancyController', ['$scope', 'VacancyService', function ($scope, VacancyService) {
$scope.GetVacancies = function () {
$scope.vacancieslist = [];
var getData = VacancyService.Vacancies();
getData.then(function (ord) {
angular.forEach(ord.data, function (val) {
if ($.trim(val).length > 0) {
var obj = new Object();
obj.name = val.VacTitle;
obj.id = val.VacNo;
if (val.VacNo > 0) {
$scope.vacancieslist.push(obj);
}
}
});
}, function () {
genericService.warningNotify("Error in getting List of Vacancies");
});
}
$scope.GetVacancies();
}]);
It is possible for one controller to call another controller, as shown in the following stack overflow(Can one controller call another?). You can use emit or broadcast depending on whether they are a child of another.
However in your case, it is better for getAllData to be placed in a service. The service would return the result of getAllData. In the service you can then either always return the result of getAllData from a http call, or cache the service (by setting it to a variable and returning it)
app.service('commonSvc', function() {
var getAllData = function{... //what you have in your code}
return {
getAllData:getAllData
}
}
By having this commonSvc, you can inject and invoke this service each time you need to call this function, similar to what you are doing for VacancyService. Keep in mind, all service in Angular are singletons. In vacancyController you can then introduce a new function onChange which calls commonSvc.getAllData.
I am trying to set the controllers scope variable to the data, however no luck. The console.log in the controller shows undefined. Appreciate the help!
My angular service has the following code --
service('VyrtEventService', ['$http', function($http) {
var events = [];
this.getArtistEvents = function(artistId) {
var url = '/api/users/' + artistId + '/';
var promise = $http.get(url).success(function(data, status, headers, config) {
events = data.artist.events;
console.log(events);
return events;
}).catch(function(error) {
status = 'Unable to load artist data: ' + error.message;
console.log(status);
});
return promise;
};
}]);
And I am referencing it in the controller as follows --
VyrtEventService.getArtistEvents($scope.artistId).then(function(data){
$scope.events = data.data.artist.events;
});
console.log($scope.events);
You should just set $scope.events = data in your controller cause your promise already returns data.artist.events when it resolves
To pass scope to service from anywhere in controller. Make sure you inject service .
controllersModule.controller('MyCtrl', function($scope, $filter, $http, $compile, ngTableParams, **FactoryYouwant**)
{
**FactoryYouwant**.getdata($scope.**scopeYoutwantTopass**).then (function(responseData){
var ResponseFromServer =responseData.data;
}
in service
controllersModule.factory('**FactoryYouwant**, function($http) {
var responseData = null;
return {
getdata : function(**data**){ (you dont have to use $)
responseData = $http.post or whatever actually gets you data;
return responseData;
}
};
});
I hope this helps you to call get data from service anywhere in controller.
I have an factory that gets data from my backend:
as.factory("abbdata", function GetAbbData($http,$rootScope,$routeParams,$q) { //$q = promise
var deffered = $q.defer();
var data = [];
var abbdata = {};
abbdata.async = function () {
$http.get($rootScope.appUrl + '/nao/summary/' + $routeParams['id']).success(function(d) {
data = d.abbData;
deffered.resolve();
});
return deffered.promise;
};
abbdata.data = function() {
return data;
};
return abbdata;
});
A call my factory like this in my controller:
abbdata.async().then(function() {
$scope.abbData = abbdata.data(); //Contains data
});
When I do a console.log($scope.abbData) outside my service call, just underneath, the result Is undifined. Why? Should not the $scope.abbData contain the data from my service after I call it?
EDIT:
You need to pass the data that should be returned into the resolve function like this:
deffered.resolve(data);
EDIT:
To get the data in the controller do this:
abbdata.async().then(function(data) {
$scope.abbData = data; //Contains data
});
Why don't you simply return that value from the async call in the first place?
You can chain promises so by attaching a success handler in your factory and returning a value from that you can simplify your code to:
as.factory("abbdata", function GetAbbData($http,$rootScope,$routeParams) {
return {
async: function () {
return $http.get($rootScope.appUrl + '/nao/summary/' + $routeParams['id']).success(function(d) {
return d.data.abbData;
});
}
}
});
And then use it like
abbdata.async().then(function(data) {
$scope.abbData = data; //Contains data
});
if you console.log($scope.abbData) outside the service call it should show undefined, since the call is asynchronous.
abbdata.async().then(function() {
$scope.abbData = abbdata.data(); //Contains data
});
console.log($scope.abbData) // this should show undefined
The console.log($scope.abbData) just after setting the abbData should show the data
abbdata.async().then(function() {
$scope.abbData = abbdata.data(); //Contains data
console.log($scope.abbData) // this should show the data
});
EDIT
you can use abbData from your service call like for example
angular.module('myApp', []).controller('HomeCtrl', function($scope, abbdata){
var updateUI;
$scope.abbData = [];
abbdata.async().then(function() {
$scope.abbData = abbdata.data(); //Contains data
updateUI();
});
updateUI = function(){
//do something with $scope.abbData
}
});
EDIT 2
On response to your query, I would do something like,
angular.module('myApp', [])
.controller('JobsCtrl', function($scope, $jobService) {
$scope.jobs = [];
$jobService.all().then(function(jobs) {
$scope.jobs = jobs;
});
})
.service('$jobService', function ($q, $http) {
return {
all: function () {
var deferred = $q.defer();
$http({
url: 'http://url',
method: "GET"
}).success(function (data) {
deferred.resolve(data);
}).error(function () {
deferred.reject("connection issue");
});
return deferred.promise;
}
}
});
associated view
<body ng-app = "myApp">
<div ng-controller = "JobsCtrl">
<div ng-repeat="job in jobs track by job.id">
<a href="#/tab/jobs/{{job.id}}" class="item item-icon-right">
<h2>{{job.job_name}}</h2>
<p>DUE DATE: {{job.job_due_date}}</p>
</a>
</div>
<div>
</body>
Here the service an all function which returns a promise, i.e. it will notify when data is fetched.
in the controller the service is called and as soon the service call is resolved the $scope.jobs is assigned by the resolved data.
the $scope.jobs is used in the angular view. as soon as the jobs data are resolved, i.e. $scope.jobs is assigned, the view is updated.
hope this helps
I had a quick look, I have 2 ideas:
First theory: your service is returning undefined.
Second theory: you need to run $scope.$apply();
See this fiddler: https://jsfiddle.net/Lgfxtfm2/1/
'use strict';
var GetAbbData = function($q) {
//$q = promise
var deffered = $q.defer();
var data = [];
var abbdata = {};
abbdata.async = function () {
setTimeout(function() {
//1: set dummy data
//data = [200, 201];
//2: do nothing
//
//3: set data as undefined
//data = undefined;
deffered.resolve();
}, 100);
return deffered.promise;
};
abbdata.data = function() {
return data;
};
return abbdata;
};
var abbdata = GetAbbData(Q)
abbdata.async().then(function() {
console.log(abbdata.data()); //Contains data
});
I have stripped away a lot of dependencies and replaced $q with Q just for my own ease.
In the above example, I first attempted to run the code with dummy data, the console output the expected data, then I tried to not assign the data, and I get an empty array. This is why I assume that if you are seeing 'undefined' you must be explicitly setting the value to 'undefined'.
That aside, I also noticed that you were testing the result by reading directly from $scope. I know that when not inside the angular scope, doing operations on the $scope object does not necessarily happen in a timely manner, and typing $scope.$apply() usually fixes this. Usually, when using $http, angular keeps you in the appropriate scope, but you are creating your own promise using $q so this could be another potential issue.
Finally, the other two answers have pointed out that you are not using promises in the standard way. Although your code works fine, it is not normal to set your data directly onto your service and retrieve it from there. You can keep your service stateless by simply resolving your promise with the data that you want to process in the then method as shown by the answers by Anzeo and Markus.
I hope I was able to find the solution, good luck.
Dipun
as.factory("abbdata", function GetAbbData($http,$rootScope,$routeParams,$q) { //$q = promise
var deffered = $q.defer();
var data = [];
var abbdata = {};
abbdata.async = function () {
$http.get($rootScope.appUrl + '/nao/summary/' + $routeParams['id']).success(function(d) {
data = d.abbData;
deffered.resolve(data);
});
return deffered.promise;
};
abbdata.data = function() {
return data;
};
return abbdata;
});
I'm trying to create a controller that gets data from Google app engine and allows me to display it on a page. The problem seems to be that the data (resp) can be accessed locally, but I can't seem to access it outside of the function. I am able to do so if I simply use javascript (...document.getElementById('getListingsResult').innerHTML = result;...), but if I invoke $scope for Angular, I can't access it any longer. Does anyone have any idea of how I can fix it while retaining the same structure to load and call gapi? Heres' my code:
(edit: added $scope.loadData, but problem persists)
phonecatControllers.controller('datastoreTestCtrl', ['$scope',
function($scope) {
$scope.data;
$scope.loadData = function() {
var ROOT = 'https://my_team.appspot.com/_ah/api';
gapi.client.load('listingserviceapi', 'v1', function(){
console.log("reached step 1");
var request = gapi.client.listingserviceapi.getListings();
request.execute(function (resp){
if (!resp.code) {
// console.debug(resp);
console.log('loaded! :)');//returns loaded
resp.items = resp.items || [];
$scope.data = resp.items;
console.log($scope.data); //returns an array of data
}
};
} , ROOT );};
$scope.loadData;
console.log($scope.data); //returns [object, object] which is incorrect
}]);
It should work using promise. Also, there is a missing parenthesis for request.execute function in your code.
Check the below code (untested):
phonecatControllers.controller('datastoreTestCtrl', ['$scope', '$q',
function ($scope, $q) {
$scope.data = null;
$scope.loadData = function () {
var deferred = $q.defer(),
ROOT = 'https://my_team.appspot.com/_ah/api';
gapi.client.load('listingserviceapi', 'v1', function () {
console.log("reached step 1");
var request = gapi.client.listingserviceapi.getListings();
request.execute(function (resp) {
if (!resp.code) {
// console.debug(resp);
console.log('loaded! :)'); //returns loaded
resp.items = resp.items || [];
//$scope.data = resp.items;
//console.log($scope.data); //returns an array of data
deferred.resolve(resp.items);
}
}); //---missing parenthesis here
}, ROOT);
return deferred.promise;
};
$scope.loadData().then(function (data) {
$scope.data = data;
console.log($scope.data); //returns [object, object] which is incorrect
});
}]);
That is because you are doing asynchronous call. When you trying to access $scope.data from outside of your callback your request is not finished yet it is still in process. You have to make sure that your request is done.
On my web application, there are two kinds of users: guests & logged. The main page loads the same content for each.
My goal :
When a registered user clicks the link, 2 ajax requests ($http) retrieve
the data of another page and load them in a model.
If the user is a guest, another model appears saying that he has to register.
My link :
<h4 ng-click="guestAction($event, showOne($event,card.id));">click me</h4>
GuestAction :
$scope.guestAction = function($event, callbackB) {
$http.get('/guest/is-guest/').success(function(data) {
console.log("isGuest retrieved : " + data);
if (data == 1)
{
alert('guest spotted !');
return false;
}
else
{
alert('user');
console.log(callbackB);
eval('$scope.'+callbackB);
}
});
}
This way, if a guest is spotted, we return false and stop the execution. If it's a regular user, we execute the function showOne. As I want to do 2 asynchronous requests one after the other, I chose to use the callback trick.
The problem is that showOne() is executed directly when ng-click is launched. I tried to pass showOne() as a string, and eval() the string in GuestAction, but the parameters become undefined...
Any idea how to solve this problem? I want to use a generic method which fires a function only if the user is logged.
I would recommend using a service and promises, see this AngularJS $q
You don't have to use a service for $http requests but that is just my preference, it makes your controller a lot cleaner
Here is the service with the promise:
app.factory('myService', function ($http, $q) {
var service = {};
service.guestAction = function () {
var deferred = $q.defer();
$http.get('/guest/is-guest/').success(function(data) {
console.log("isGuest retrieved : " + data);
if (data == 1) {
deferred.resolve(true);
} else {
deferred.resolve(false);
}
}).error(function (data) {
deferred.reject('Error checking server.');
});
return deferred.promise;
};
return service;
});
And then in our controller we would call it something like so:
app.controller('myController', function ($scope, myService) {
$scope.guestAction = function($event, card) {
myService.guestAction().then(function (data) {
if (data) {
alert('guest spotted !');
} else {
alert('user');
// Then run your showOne
// If this is also async I would use another promise
$scope.showOne($event, card.id);
}
}, function (error) {
console.error('ERROR: ' + error);
})
};
});
Now obviously you may have to change things here and there to get it working for your needs but what promises do is allow you to execute code and once the promise is returned then continue, I believe something like this is what you are looking for.
You have to pass functions as parameters without the parenthesis and pass in the parameters separately:
<h4 ng-click="guestAction($event,card.id, showOne);">click me</h4>
and
$scope.guestAction = function($event,id, callbackB) {
$http.get('/guest/is-guest/').success(function(data) {
console.log("isGuest retrieved : " + data);
if (data == 1)
{
alert('guest spotted !');
return false;
}
else
{
alert('user');
callbackB($event,id);
}
});
}