Cannot receive data from factory in controller (Angular) - angularjs

Why second console.log gives me undefined when first one prints actual data? How to assign data received in promise to any variable to be able to use it without promise?
angular.module('dopasujApp')
.controller('productLoader', [ '$scope','getProducts',
function ($scope,getProducts) {
$scope.recived = {};
console.log($scope.list);
function getter() {
return getProducts(1, 150, 20, 'ASC', 'PRODUCT_ID', 'a').then(function (recived) {
$scope.recived = recived;
console.log($scope.recived);
}
);
}
getter();
console.log($scope.recived);
}
]);
Here is my factory code:
angular.module('dopasujApp').factory('getProducts', ['$http', function ($http) {
return function(fit = 1, limit = 150, vendor = 20, sort = 'ASC', orderBy = 'PRODUCT_ID', search='a', size=[], filters={}, attribs=[]) {
var getProduct = {}, recived = {}, APIurl;
getProduct.data={};
getProduct.data.sort=sort;
getProduct.data.orderBy=orderBy;
getProduct.data.search=search;
getProduct.data.filters=filters;
getProduct.data.filters.ATTRIBS=attribs;
getProduct.data.filters.SIZE=size;
recived.list= [];
recived.count = 0;
recived.filters = {};
APIurl = "*******?action=getListing&fit="+fit+"&limit="+limit+"&vendor="+vendor;
var request = $http({
method: "POST",
url: APIurl,
headers: {
'Content-Type': 'application/json'
},
data: {
data: getProduct.data
}
});
var values = request.then(function (response){
return {
list: response.data.QUERY,
count: response.data.COUNT,
filters: response.data.FILTERS
};
});
return values;
}
}]);

Because your getProducts is a promise, and even tough your second console.log is after your getter();, the console.log is executed before your promise is resolved, thats why its undefined.

Related

$q.all() returns an array of undefined values

I have 3 functions that request data from backend. Following is the code in the controller.js file
$scope.getList1 = = function () {
service.getList1(constants.getUrl1).then(
function success(data) {
$scope.list1 = data.data.list1;
return data;
}, function error() {
});
};
$scope.getList2 = = function () {
service.getList2(constants.getUrl2).then(
function success(data) {
$scope.list2 = data.data.list2;
return data;
}, function error() {
});
};
$scope.getList3 = = function () {
service.getList3(constants.getUrl3).then(
function success(data) {
$scope.list3 = data.data.list3;
return data;
}, function error() {
});
};
And in my service.js file, I have $http requests to fetch the data from the server. Following is my code in the service.js
this.getList1 = function (getUrl1) {
return $http({
method: 'GET',
url: getUrl1
});
};
this.getList2 = function (getUrl2) {
return $http({
method: 'GET',
url: getUrl2
});
};
this.getList3 = function (getUrl3) {
return $http({
method: 'GET',
url: getUrl3
});
};
then I in a separate function I have called like below
$scope.initialise = function () {
var requestArray = [$scope.getList1(), $scope.getList2(), $scope.getList3()];
$q.all(requestArray).then(function (response) {
console.log(response);
//other logic after successful completion of all 3 requests
});
};
But in the response, I get an array of 3 'undefined' values
e.g [undefined, undefined, undefined]
What am I doing wrong here. Any suggestions ?.
Thanks.
Add return statement in functions in your $scope.
$scope.getList1 = = function () {
/** here-> */ return service.getList1(constants.getUrl1).then(
function success(data) {
$scope.list1 = data.data.list1;
return data;
}, function error() {
});
};
And so on.
Issue is that your functions in service are returning promises, but functions in scope are not returning anything.

Call rest webservice and assign value to variable in Angularjs

I have defined a function in my controller which calls a rest web-service in AngularJs. I need to assign the value returned by the function to a variable. The function may be called many times with different parameters and assign the value returned to different variables.
The function in my controller takes a parameter, which in turn is then passed to the web-service.
function disableBlockFn(blocKName) {
var data = {
formId: vm.formId,
blockId: blocKName,
statusId: vm.statusId,
roleId: vm.roleId
}
roleService.displayBlock(data).then(function (data) {
return data[0];
});
}
When i assign the value of the aboved function to a variable and print it on the console, i get 'Undefined'.
vm.pilot = vm.disableBlockFn('pilot_mpr_qualite_1');
console.log('pilot ', vm.pilot);
vm.pilot_ang = vm.disableBlockFn('pilot_mpr_qualite_ang');
console.log('pilot ', vm.pilot_ang);
Function to request WS
function displayBlock(objBlock) {
var defer = $q.defer();
$http({
method: 'GET',
url: config.urlPath + 'api/block/block'
}).then(function (data) {
var results = data;
defer.resolve(results.data.data);
}, function (data, status) {
defer.reject({
data: data,
status: status
});
});
return defer.promise;
}
You need to return a promise from disableBlockFn. If your function is in your controller, you can set the value directly.
function disableBlockFn(blocKName) {
var data = {
formId: vm.formId,
blockId: blocKName,
statusId: vm.statusId,
roleId: vm.roleId
}
//here you can return only your promise
return roleService.displayBlock(data);
}
And then, resolve the promise in your controller:
vm.disableBlockFn('pilot_mpr_qualite_1').then(function(data) {
vm.pilot = data;
console.log('pilot ', vm.pilot);
});
EDITED:
If your function is in your controller you can do this:
function disableBlockFn(blocKName) {
var data = {
formId: vm.formId,
blockId: blocKName,
statusId: vm.statusId,
roleId: vm.roleId
}
return roleService.displayBlock(data)
.then(function(data){
vm.pilot = data[0];
});
}

Send by post with restangular

I'm trying to send by post a json with restangular but this don't return me anything.
Code:
$scope.userPost = {
title: 'foo',
body: 'bar',
userId: 1
}
$scope.registerUser=function(){
$scope.people = Restangular.all('post').post($scope.userPost);
console.log($scope.people);
}
It return me this:
{ "restangularCollection": false, "$object": {} }
http://jsonplaceholder.typicode.com/post 404 (Not Found)
Usually I use this with ajax and it return me a json:
$.ajax('http://jsonplaceholder.typicode.com/posts', {
method: 'POST',
data: {
title: 'foo',
body: 'bar',
userId: 1
}
}).then(function(data) {
console.log(data);
});
Any ideas ?
Restangular works with promises, so, you should be doing something like:
$scope.registerUser = function () {
Restangular.all('post').post($scope.userPost).then((data) => {
$scope.people = data;
console.log($scope.people);
});
}
Or you can take advantage of its enhanced promises and do something like:
$scope.registerUser = function () {
$scope.people = Restangular.all('post').post($scope.userPost).$object;
}
In Angular 4+
constructor(private restangular: Restangular){}
// latter in your method
const baseUsers = this.restangular.all('post');
baseUsers.post({'title':'foo','body': 'bar','userId': '1'})
.subscribe(data => { console.log(data); });

angularjs return another promise in $q.all

in my angularjs/ionic application I'm getting data from a sqlite database and want to get data from a web service which depends on the values of the database variables. I thought this might be possible with:
.factory('WebSrvs', function($http,DatabaseSrvs) {
var getData = function() {
var promiseReceiverUrl = DatabaseSrvs.getLocalValue('value1');
var promiseVehicleId = DatabaseSrvs.getLocalValue('value2');
$q.all([promiseReceiverUrl,promiseVehicleId]).then(function(results) {
if(results[0].rows.length > 0 && results[1].rows.length > 0) {
var v1 = results[0].rows.item(0).Value;
var v2 = results[1].rows.item(0).Value;
var req = {
method: 'POST',
url: v1,
headers: {
'Content-Type': 'application/json; charset=utf-8'
},
data: {
value: v2
},
timeout: 10000,
crossDomain: true
};
return $http(req);
}
});
}
}
But if do it this way and call it with var promise1 = WebSrvs.getData(); I don't get anything back and the app runs in a reloading queue which never ends. What am I doing wrong?
To add to my comments:
.factory('WebSrvs', function($http,DatabaseSrvs) {
var getData = function() {
var promiseReceiverUrl = DatabaseSrvs.getLocalValue('value1');
var promiseVehicleId = DatabaseSrvs.getLocalValue('value2');
return $q.all([promiseReceiverUrl,promiseVehicleId]).then(function(results) {
if(results[0].rows.length > 0 && results[1].rows.length > 0) {
var v1 = results[0].rows.item(0).Value;
var v2 = results[1].rows.item(0).Value;
var req = {
method: 'POST',
url: v1,
headers: {
'Content-Type': 'application/json; charset=utf-8'
},
data: {
value: v2
},
timeout: 10000,
crossDomain: true
};
return $http(req);
}
});
}
return {
getData: getData
};
}
In your factory implementation you need to first return getData function. Also inside getData function you have to return $q.all.
.factory('WebSrvs', function($http,DatabaseSrvs) {
var getData = function() {
var promiseReceiverUrl = DatabaseSrvs.getLocalValue('value1');
var promiseVehicleId = DatabaseSrvs.getLocalValue('value2');
return $q.all([promiseReceiverUrl,promiseVehicleId]).then(function(results) {
...
...
});
}
return {
getData: getData
}
}

AngularJS and complex JSON returned by django tastypie

I have few resources written on AngularJS that access a Tastypie API. Everything works fine, except for a detail: tastypie always encapsulate the actual result inside a objects attribute on a JSON, example:
/api/v1/reminder/:
{
meta: {
limit: 20,
next: null,
offset: 0,
previous: null,
total_count: 3
},
objects: [{
category: {
color: "#999999",
id: 1,
name: "Groceries",
resource_uri: "/api/v1/category/1"
},
description: "",
due_date: "2010-10-16",
id: 1,
repeat: "weekly",
resource_uri: "/api/v1/reminder/1",
value: "-50"
}, {
category: {
color: "#999999",
id: 1,
name: "Groceries",
resource_uri: "/api/v1/category/1"
},
description: "",
due_date: "2010-10-17",
id: 2,
repeat: "weekly",
resource_uri: "/api/v1/reminder/2",
value: "-50"
}
}
It was wasy to fix using a callback to the get() call:
Reminder.get().$then(function (result) {
$scope.reminders = result.data.objects;
});
But I know result.resource is an actual Reminder instance.
.factory('Reminder', ['$resource', function($resource){
var Reminder = $resource('/api/v1/reminder/:id', {}, {
get: {
method: 'GET',
isArray: false
}
});
Reminder.prototype.TESTE = function () {console.log('asd');};
return Reminder;
}])
Now I need to implement behavior on my Reminder class, and I need every element on my meta.objects to be an instance of Reminder:
Reminder.get().$then(function (result) {
$scope.reminders = result.data.objects;
result.resource.TESTE(); // -> outputs 'asd'
o = result.data.objects[0];
o.TESTE // -> undefined, obvisously
i = new Reminder(o);
i.TESTE() // -> outputs 'asd'
});
So, how to I get angularjs to understand that every object on objects is the actual result so it behaves like a list of instances?
The workaround is to creating a new list iterating on the results creating the instances, but it's not optimal...
Suggestions?
Solution by #rtcherry:
As suggested by rtcherry, I used restangular
Configuring the reading of request data:
.config(['RestangularProvider', function(RestangularProvider) {
RestangularProvider.setBaseUrl("/api/v1");
RestangularProvider.setResponseExtractor(function(response, operation, what, url) {
var newResponse;
if (operation === "getList") {
newResponse = response.objects;
newResponse.metadata = response.meta;
} else {
newResponse = response.data;
}
return newResponse;
});
}])
Loading the reminders:
function RemindersCtrl ($scope, $rootScope, Reminder) {
$scope.reminders = Reminder.getList();
}
Adding my custom method to Reminder (not as clean as ngResource, but doable):
.factory('Reminder', ['Restangular', '$filter', function(Restangular, $filter){
var Reminder = Restangular.all('reminder');
var remainingDays = function () {
//do stuff
};
// adding custom behavior
Restangular.addElementTransformer('reminder', false, function (reminder) {
reminder.remainingDays = remainingDays;
return reminder;
});
return Reminder;
}])
Solution by #moderndegree:
I used pure ngResource:
var tastypieDataTransformer = function ($http) {
return $http.defaults.transformResponse.concat([
function (data, headersGetter) {
var result = data.objects;
result.meta = data.meta;
return result;
}
])
};
...
.factory('Reminder', ['$resource', '$http', function($resource, $http){
var Reminder = $resource('/api/v1/reminder/:id', {}, {
query: {
method: 'GET',
isArray: true,
transformResponse: tastypieDataTransformer($http)
}
});
Reminder.prototype.remainingDays = function () {
// doing stuff
};
return Reminder;
}])
My controller:
Transaction.query(filter).$then(function (result) {
$scope.days = [];
var transactions = result.resource;
resource[0].remainingDays(); // it works
});
If you wanted to avoid using an additional library, you should be able to do the following:
$resource('/api/v1/reminder/', {}, {
query: {
method: 'GET',
isArray: true,
transformResponse: $http.defaults.transformResponse.concat([
function (data, headersGetter) {
return data.objects;
}
])
}
});
This will append your transform to $HttpProvider's default transformer.
Note: Correct me if I'm wrong on this one but I believe this feature requires v1.1.2 or greater.
You may want to try something like restangular.
There is some configuration needed to make that work. An example is here.
I ended up doing the following in order to preserve the meta object directly on the results returned by Reminder.query() (expanding on the answer from #moderndegree).
var Reminder = $resource('/api/v1/reminder/', {}, {
query: {
method: 'GET',
isArray: true,
transformResponse: $http.defaults.transformResponse.concat([
function (data, headersGetter) {
return data.objects;
}
]),
interceptor: {
response: function(response) {
response.resource.meta = response.data.meta;
}
}
}
});
That allows you to get the meta value directly on the returned object:
var reminders = Reminder.query(...);
console.log(reminders.meta); // Returns `meta` as expected.
I think it would also be possible to do something similar inside the callback from Reminder.query since the response object is available there as well.

Resources