Angularjs custom service variable undefined by $http success - javascript

I wrote an angular service that is supposed to load customer details over http and store them for future use (in a singleton pattern).
However, when the http call returns successfuly, the variable into which I want to inject the data is undefined and an error is thrown.
Here's the code:
NBApp.service("serverDataService", ['$http', '$q', function ($http, $q) {
var currentMemberDetails = [];
this.currentMember = function (){
if (this.currentMemberDetails.length>0)
return this.currentMemberDetails[0];
return null;
};
this.getMemberDetails = function (cardId) {
//TBD - call members details web service
var deferred = $q.defer();
$http({
method: 'GET',
url: 'resources/customer.json'
}).then
(
function success(response) {
this.currentMemberDetails.push(response.data);
deferred.resolve(data);
},
function failure(response) {
deferred.reject(response);
}
);
return deferred.promise;
};
}]);
The currentMemberDetails is undefined, as shown by the chrome dev console.

Instead of
this.currentMemberDetails.push(response.data);
Use
currentMemberDetails.push(response.data);

Related

AngularJS - Unable to call http serrvice using factory

To resolve my issue I have gone through many articles on different sites, but none resolved it.
I'm writing a simple AngularJS application. I'm quite new to Angular. I have written a factory method which call the $http service which gets the data from the web api. Web api is running fine and its returning the JSON object as expected.
Angular Code
var app = angular.module("app", [])
.controller("controller", function ($scope, WebFactory) {
$scope.data = "data";
$scope.error = "error";
$scope.data=WebFactory.getData();
})
.factory("WebFactory", function ($http) {
var obj = {};
obj.getData = function()
{
$http({
method: "GET",
url: "http://localhost:37103/api/employee",
}).then(function success(response) {
return response.data;
})
.then(function error(response) {
return response;
});
return 'No data';
}
return obj;
});
HTML code
<body ng-controller="controller">
data: {{data}}
<br/>
error: {{error}}
<br />
I have spent 2 days, but still dont know why its not working.
Try something like this instead:
var app = angular.module("app", [])
.controller("controller", function ($scope, WebFactory) {
$scope.data = "data";
$scope.error = "error";
$scope.data = {}
WebFactory.getData().then(function success(response) {
$scope.data = response.data;
});
})
.factory("WebFactory", function ($http) {
var obj = {};
obj.getData = function()
{
return $http({
method: "GET",
url: "http://localhost:37103/api/employee",
})
}
return obj;
});
First of all, you're missing an ng-app declaration
Secondly, you are misusing the then callback. It accepts three parameters: success callback, error callback, finally callback.
As the first then executes the success, then it executes the second callback that always returns the response, but I assume it is not intended to be that way and you could use the get function which is more easy to use.
It should be:
$http.get("http://localhost:37103/api/employee").then(function(response){
//Do something with successfull response
},
function(error){ //do something with the error});
See documentation about promises
Lastly, you are dealing with promises and async code, yet returning response or string instead of the promise with the result. So the getData() should look like this:
obj.getData = function(){
return $http.get("http://localhost:37103/api/employee");
}
And use the then(success,error,finally) callbacks in the controller or if you want to provide values on the error/finally callbacks in the service, you should use the $q service
obj.getData(){
var deferred = $q.defer();
$http.get(...).then(function(response){
deferred.resolve(response.data);
},
function(error){
deferred.reject("No Data");
});
return deferred.promise;
}
Of course you would have to provide $q service to the WebFactory

How do you deal with asynchronous return from $http.post in angularJS?

Stuck with a simple basic login problem here. My AuthService factory has following code inside of it (2 relevant functions and a local variable):
var username = '';
function login(uname, upwd, utype) {
// create a new instance of deferred
var deferred = $q.defer();
$http({
method: 'POST',
url: '/root',
headers: {
'Content-Type': 'application/json'
},
data: {
username: uname,
password: upwd,
type: utype
}
}).success(function(data, status, headers, config) {
if (status === 200) {
user = true;
username = data.username;
usertype = data.usertype;
deferred.resolve();
} else {
user = false;
deferred.reject();
}
})
.error(function(data, status, headers, config) {
user = false;
deferred.reject();
});
// return promise object
return deferred.promise;
}
function getusername() {
return username;
}
My controller looks like this:
angular.module('smApp').controller('rootloginController', ['$scope', '$location', 'notificationFactory', 'AuthService',
function($scope, $location, notificationFactory, AuthService) {
$scope.submit = function() {
AuthService.login($scope.rEmail, $scope.rootPassword, 'root')
if (AuthService.isLoggedIn()) {
$location.url('/dashboard');
notificationFactory.success('Logged in as ' + rootEmail);
} else {
//ngNotifier.notifyError($scope.rEmail);
notificationFactory.error('Invalid username & password combination');
}
};
};
}]);
I am calling my getusername() in the if statementright after login() and since login has $http post it's asynchronous and I think im hitting a wall here.
So my main problem here is the first click always gives me error message and the second clicks logs me in. I am assuming this has to do with the promise not being fulfilled right away and taking some time to execute. I was wondering if there was anyway around this? I really dont have any other code to execute beside wait since this is a login page and using a timeout doesnt seem like the proper way to do it.
In this case you need to use the Promise API. Calls to the server made via the $http service return a promise, which allow binding of .success and .error methods.
The .then method may be used as a shorthand for both .success and .error. It accepts two functions that it executes in success and error scenarios respectively. Returning a promise in those functions allows chaining calls to the server.
In most cases, this should suffice:
// In service
login: function () {
return $http.post('your:url').then( // `then` here is optional, but possible
function () {}, // update service values without having to involve the controller (and/or transform the response object)
function () {} // throw error log mesages
)
}
// In controller
$scope.submit = function () {
AuthService.login().then(
function () {
// success logic: redirect, assign scope variables, etc
},
function () {
// error logic: allow retry
}
);
}
You have to call AuthService.isLoggedIn() after the login request has been completed. For this, first return the promise of the deferred object you created.
function login(uname, upwd, utype) {
// create a new instance of deferred
var deferred = $q.defer();
$http({
method: 'POST',
...
return deferred.promise;
}
Now, you can wait for the request to complete.
AuthService.login($scope.rEmail, $scope.rootPassword, 'root').finally(function() {
if (AuthService.isLoggedIn()) {
$location.url('/dashboard');
notificationFactory.success('Logged in as ' + rootEmail);
} else {
//ngNotifier.notifyError($scope.rEmail);
notificationFactory.error('Invalid username & password combination');
}
});

Angularjs to return $http response to caller Object

How can I pass the response value to the parent object. The invoker who make the http service call in angularjs? What I have is a BaseModel where it will do the get like following. The idea is basemodel object instance should have the reponse values. BTW i am trying to avoid to use broadcast and on.
To invoke the object
model = new BaseModel();
model.get();
Definition:
BaseModel.$service = ['$http', '$q',
function ($http, $q) {
return function () {
return new BaseModel($http, $q);
};
}];
the actual BaseModel:
function BaseModel($http, $q) {
var q = $q.defer();
this.http = $http;
this.response = null // this is to hold the response value
this.get = function () {
var request = this.http({
url: 'http://blahblah.com?a=1&b=2',
method: "GET",
});
request.success(function (response) {
q.resolve(response);
});
q.promise.then(
function(response){
console.log(response, ' got response');
//the idea is to have this.response = response
return response;
}
);
return q.promise
};
You need to use a self variable so you can refer to the BaseModel's instance variables:
function BaseModel($http, $q) {
var self = this;
self.response = null;
/* ... rest of code ... */
q.promise.then(function (response) {
console.log(response, ' got response');
self.response = response;
});
/* ... rest of code ... */
}
The issue isn't angularjs related ,it's related to how objects work in JavaScript and how you have to create a separate self reference because this refers to the inner-most function.

returning value to higher scope inside from a chained functions in angular js

I have a function getUsers that I want to return JSON from an web api .. but I cant seem to get the data out, I can't return the data because the function is inside $http. What should i do ?
function getUsers() {
$http({ method: 'GET', url: '/api/loginapi/userdetails' })
.success(function (data, status, headers, config) {
details = data;
});
return details;
}
$http makes an asynchronous call, so you can't immediately return fetched data.
What you can return is a promise. Good news, $http() returns one:
function getUsers() {
return $http({ method: 'GET', url: '/api/loginapi/userdetails' });
}
Then you can use your function:
getUsers().then(function(data) {
var details = data;
// Process your details!
});
You can access data there for sure, nothing is 'inside' $http, I suggest you pass in a callback to getUsers() and do whatever you want with the returned data:
var getUsers=function(callback){
$http({ method: 'GET', url: '/api/loginapi/userdetails' })
.success(function (data, status, headers, config) {
callback(data);
});
}
and use it like this inside your controller:
getUsers(function(users){
$scope.whatever = users;
})
I created plunkr for your question http://plnkr.co/edit/lqX4apmvnKM3i5jGZDQM?p=preview
$http return a promise. Please read details about promise at http://johnmunsch.com/2013/07/17/angularjs-services-and-promises/ . It is an important understanding if you want to write asynchronous application. This is another useful link - Inside my own Angular service, how do you get the return value back to the Controller that called the service? . Refer to answer. It explains how to use promise and deffered in angularjs.
var app = angular.module('plunker', []);
app.service('UserService', ['$http', '$q',
function($http, $q) {
this.getUser = function() {
return $http({
method: 'GET',
url: 'data.json'
});
};
}
]);
app.controller('MainCtrl', function($scope, UserService) {
$scope.user = {};
UserService.getUser().then(function(response){
$scope.user = response.data;
});
});

unable to get data from $q into scope

I'm trying to bind some data being returned from an API to my scope using promises with $q, I am able to pull the data from the server without any issue (I can see JSON being returned using fiddler) however the $scope variable remains empty, any help would be greatly appreciated! Thanks in advance.
Code:
toDoListService.js
app.factory("toDoListService", function ($http, $q) {
var deferred = $q.defer();
return {
get: function () {
$http({ method: 'GET', url: '/api/todo/' }).
success(function (data) {
deferred.resolve(data);
}).
error(function (data, status, headers, config) {
deferred.reject(status);
});
return deferred.promise;
}
});
toDoListController.js
app.controller("toDoListController", function($scope, toDoListService){
$scope.toDoList = toDoListService.get();
});
First of all you should put var deferred = $q.defer(); in your get function, so that every get has it's own deferred object.
Second what get actually returns is a promise. So you need to access you data in this way:
app.controller("toDoListController", function($scope, toDoListService){
toDoListService.get().then(function(data){
$scope.toDoList = data;
});
});
Right now, your $scope.toDoList is bound to a promise. This means of binding used to work, but was deprecated in, I think, 1.2.
As Michael suggests, you must do:
app.controller("toDoListController", function($scope, toDoListService){
toDoListService.get().then(function(data){
$scope.toDoList = data;
});
});
Also, using $q is not required here at all, as $http returns a promise anyway. Therefore, you could just do:
app.factory("toDoListService", function ($http){
return {
get: function () {
return $http({ method: 'GET', url: '/api/todo/' });
}
};
});
You can simplify your code by using this:
toDoListService.js
app.factory("toDoListService", function ($http, $q) {
return {
get: function () {
return $http({ method: 'GET', url: '/api/todo/' });
}
}
});
toDoListController.js
app.controller("toDoListController", function($scope, toDoListService) {
toDoListService.get().then(function(response){
$scope.toDoList = response.data;
return response;
});
});
Be sure to return response in your success callback, otherwise chained promises would not receive it.

Categories

Resources