Angular 1.5 scope variable undefined in $http service success callback - angularjs

Given the following Angular 1.5 Component Controller...
'use strict'
function MyController($http){
//The template variable for cloning on a form
this.myVarTemplate = {};
//The Data I wish to send to the server
this.myVarTemplate.data = {};
//Other form specific / callback data
this.myVarTemplate.meta = {};
this.myVarArray = [];
//A container of raw data I'm parsing for example, on whitespace bound to a text input
this.rawInput = '';
this.parse = function(){
var temp = this.rawInput.split(' ');
for( var i = 0; i < temp.length; i++){
var container = angular.copy(this.myVarTemplate);
container.data = temp[i];
this.myVarArray.push(container);
}
}
this.upload = function(){
for(var i = 0; i < this.myVarArray.length; i++){
$http({
method: 'POST',
url: <url here>,
data: this.myVarArray[i]
}).then(function(response){
//My Success callback won't work!!!!
//Response logs successfully, data is retrieved
console.log(response);
//this.myVarArray.meta is undefined??
//console.log(this.myVarArray) is undefined
this.myVarArray[i].meta.reply = response.data;
}, function(message){
//Never been an issue here
alert('Something Bad happened? doesn't happen');
}).finally(function(){
//Works
this.myVarArray[i].meta.wasSent = true;
});
}
}
})
I am trying to return a batch of AJAX query results to their proper corresponding form objects. It seems as though this.myVarArray is undefined within the context of the $http service success callback. Why is this? Is this a quirk of Angular or of Javascript itself? I understand that the $http service returns a promise, but that should be resolved within the context of the callback. Why is myVarArray undefined?
Many thanks in advance for any insight.
Edited: Fixed my example code... :)

this.myVarArray is an array of string based on what has been split from raw input in your parse. You are trying to assign an object property (.meta) to a string array element. You could try something along the lines of:
this.myVarObjArray;
this.rawInput = '';
this.parse = function(){
var temp = this.rawInput.split(' ');
var valArray = []
for( var i = 0; i < temp.length; i++){
valArray.push(angular.copy(temp[i]));
this.myVarObjArray[i] = { val: valArray};
}
}
this.upload = function(){
angular.forEach(this.myVarObjArray, function(obj,v){
$http({
method: 'POST',
url: <url here>,
data: obj.val
}).then(function(response){
//My Success callback won't work!!!!
//Response logs successfully, data is retrieved
console.log(response);
//this.myVarArray.meta is undefined??
//console.log(this.myVarArray) is undefined
obj.meta = {reply :response.data};
....
})
Essentially your trying to assign an object property to a string array element. This won't work. My syntax might not be 100%. If you pull into a plunker i'll get a working example for you. This should get you on the right track.

Related

How to concat JSON objects from different URLs with nested resources

///Returning JSON 1
$http.get("url1").
then(function (response) {
$scope.foo = response.data;
});
///Returning JSON 2
$http.get("url2").
then(function (response) {
$scope.foo = response.data;
});
///Returning JSON (n)
$http.get("n").
then(function (response) {
$scope.foo = response.data;
});
Can I somehow concat these JSON objects into one? The reason is that I have ALOT of data and since I rather would like to display alot of data for the user to filter through than to have them click through 1000 pages in a SPA, I would like to join them if that's possible (in a reasonable manner ofcourse).
EDIT
I was thinking something like this
var url ="";
for (... i < 100...) {
url = "http://url.com"+i+"";
$http.get(url).
then(function(response){
$scope.foo.concat(response.data);
}
);
}
Update
I've managed to join the JSON returns into an array of objects. But the problem is that this array now contains objects which in itself contains an object which in itself contains an array of objects... yup!
If it's array then you can concat it.
Initialize empty array first
$scope.foo = [];
$http.get("url1").
then(function (response) {
$scope.foo.concat(response.data);
});
Use $q.all to create a promise that returns an array:
function arrayPromise(url, max)
var urlArray = [];
for (let i=0; i<max; i++) {
urlArray.push(url + i);
};
var promiseArray = [];
for (let i=0; i<urlArray.length; i++) {
promiseArray.push($http.get(urlArray[i]);
};
return $q.all(promiseArray);
});
To fetch nested arrays, chain from the parent:
function nestedPromise (url, max) {
var p1 = arrayPromise(url + "item/", max);
var p2 = p1.then(function(itemArray) {
var promises = [];
for (let i=0; i<itemArray.length; i++) {
var subUrl = url + "item/" + i + "/subItem/";
promises[i] = arrayPromise(subUrl, itemArray[i].length);
};
return $q.all(promises);
});
return p2;
};
Finally resolve the nested promise:
nestedPromise("https://example.com/", 10)
.then(function (nestedArray) {
$scope.data = nestedArray;
});
It is important to use a return statement at all levels of the hierarchy: in the .then methods and in the functions themselves.
Chaining promises
Because calling the .then method of a promise returns a new derived promise, it is easily possible to create a chain of promises.
It is possible to create chains of any length and since a promise can be resolved with another promise (which will defer its resolution further), it is possible to pause/defer resolution of the promises at any point in the chain. This makes it possible to implement powerful APIs.
— AngularJS $q Service API Reference - Chaining Promises
You can wait for the n requests to finish and then do whatever you want with the object returned.
$q.all($http.get("url1"), $http.get("url2"), $http.get("url3"))
.then(function (responses) {
// This function is called when the three requests return.
// responses is an array with the first item being the result of
// fetching url1, the second fetching url2, etc.
// Depending on what the response looks like you may want to do
// something like:
$scope.data = angular.merge(responses[0], responses[1] /* etc */);
});

How to access several $http.get() and store their responses in an array using for loop and access the array

My code is like below,
// calls only the API and return it
s.getArchSales = function (url, qParam) {
// set the request object
var req = {
'method': 'POST',
'headers': securitySrv.header, // set the header
'url': url,
'data': qParam
}
return $http(req)
}
var portFolioMixArray = []
for(var i = 0; i < tech.length; i++ ){
s.getArchSales(url, query)
.then(function (response) {
portFolioMixArray.push(response.data)
})
}
tech is also an array which is also computed dynamically
Now when I console.log(portFolioMixArray) it shows Array[0] with an extension symbol at the left like below,
I can't access the Array elements. How can I access it ??
When you did console.log, at that time the http response was not yet received and the array was empty and hence you were not able to access the array elements.
Later, the response was received and array was updated. That's why you see the blue icon with the message.
You can get it by using portFolioMixArray[0]
if you want bind the values in html, then you need use ng-repeat
You should use the object in $scope
like
$scope.portFolioMixArray = []
for(var i = 0; i < tech.length; i++ ){
s.getArchSales(url, query)
.then(function (response) {
$scope.portFolioMixArray.push(response.data)
})
}
//html
<div ng-repeat="item in portFolioMixArray">{{item .YourObjectNmae}}</div>
I think you should understand the concept of promise, in Angular you can use $q.(https://docs.angularjs.org/api/ng/service/$q)
You can access your array like this:
var promises = tech.map(function (item) {
return s.getArchSales(url, query).then(function (response) {
portFolioMixArray.push(response.data)
})
}
$q.all(promises).then(function () {
console.log(portFolioMixArray);
}

Angular nested http requests

Guys i woulde like to nest http requests to flickr api, my goal is get (using flickr.photos.search) ids of photos (parametrized via 'text'), then make a second call on flick.photos.getSizes and retrieve direct link to source image.
Here is my code, but any other ideas would be nice from you. The problem is that PhotoSearchService return then all $http calls are made. Any better ideas ?
Service:
angular.module('flickrGalleryApp').factory('PhotoSearchService', function ($resource) {
const apiKey = '&api_key=my api key';
const apiUrl = 'https://api.flickr.com/services/rest/?method=';
const method = 'flickr.photos.search';
const format = '&format=json';
const callback = '&nojsoncallback=1';
return $resource(null,null, {
search : {url:apiUrl+method+apiKey+format+callback,method: 'GET', isArray: false}
});
});
Controller:
$scope.items = PhotoSearchService.search({text:'girl'}, function(response){
var array = response.photos.photo;
const apiKey = '&api_key=my api key';
const apiUrl = 'https://api.flickr.com/services/rest/?method=';
const method = 'flickr.photos.getSizes';
const format = '&format=json';
const callback = '&nojsoncallback=1';
var result = [];
for(var i = 0 ; array.length; i++){
var id = array[i].id;
var url = apiUrl+method+apiKey+format+callback + '&photo.id=' + id;
console.log(url);
$http.get(url).then(function(resp){
var array2 = resp.data.sizes.size;
for(var j = 0; j < array2.length ;j++){
if(array2[j].label.indexOf('Square') > 0){
result.push(array2[j].source);
}
}
return result;
}, function(err){
})
}
},function(error){
});
html:
<ul>
<li ng-repeat="item in items">
<span>{{item.source}}</span>
</li>
</ul>
Since you make an $http.get call for each photo id returned by the first call, you have to await for all the calls to complete then return the result.
The trick to do this is to create a defer object and resolve it when the result array is full.
Take a look at $q to see how you can pull this off. Just keep in mind that you will return a promise which means you have to implement a success or error callback to get the resulting array

AngularJs. Using $q.all for sending multiple post requests

I have an AngularJs app in which I need to make $http post calls in a loop. Here is my code:
var promisesArray = [];
for(i = 0; i < 28; i++) {
promisesArray.push($http({
method: "post",
url: "/studentanswers",
data: {
studentName: "abc",
answerImage: "sdf",
questionPrompt: 1
}
}));
}
$q.all(promisesArray).then(function(data){
console.log("success!!!!");
});
For some reason, it is not posting all the items in the collection. I am aware that browsers usually do not allow more than 6 async post calls. From what I understood, $q.all was a way around it. Also, even if I comment out the $q.all part, it doesn't matter because the post calls get executed nonetheless.
I would greatly appreciate any help!
Be aware that $q.all is not resilient. It will terminate with the first rejected promise. But that really isn't your problem.
You need to chain your posts to avoid the browser limit of simultaneous posts.
var promisesList = [];
var promiseMinusOne = $q.when();
for (var i = 0; i < 28; i++) {
//begin IIFE closure
function(i) {
//chain off promiseMinusOne
var httpPromise =
promiseMinusOne.catch (function (e) {
return e;
}) .then (function (r) {
return $http({ method: "post",
url: "/studentanswers",
data: answerList[i]
})
});
promisesList.push(httpPromise);
promiseMinusOne = httpPromise;
}(i);
//end IIFE closure
};
var chainablePromise =
promiseMinusOne.catch (function (e) {
return (e);
}) .then (function (r) {
//process the promisesList
});
Notice, that for promiseMinusOne, the use of .catch and return in order to continue the list in the event of a rejection of one of the items.

angularjs resource limit changes after first call

Problem description
Im using the angular resource to get data from my server. I've extended it a bit to make sure all of my resources have security headers.
Problem is that on the second get request and on, my get requests are sent with limit=0, and only the first get request is sent correctly (with limit=12).
Code part
This is my base resource factory (for making sure all resource contain the keys and everything):
app.factory('SecuredFactory', function($resource){
var DEFAULT_ACTIONS = {
'get': {method:'GET'},
'query': {method:'GET', isArray:true},
};
var DEFAULT_PARAMS = {
'limit': 12,
'format': 'json'
};
for(var key in DEFAULT_ACTIONS){
DEFAULT_ACTIONS[key]['headers'] = <headers object>;
}
var securedResource = function(url, paramDefaults, actions){
for (var attrname in actions) {
DEFAULT_ACTIONS[attrname] = actions[attrname];
}
for (var attrname in paramDefaults) {
DEFAULT_PARAMS[attrname] = paramDefaults[attrname];
}
var defaultResource = $resource(url, DEFAULT_PARAMS, DEFAULT_ACTIONS);
return defaultResource;
};
return securedResource;
});
And this is an example of how I creat a specific factory out of the secured one:
app.factory('QuestionFactory', function(SecuredFactory, Constants){
var url = Constants.SERVER_URL + 'question/';
var Task = SecuredFactory(url);
return Task;
});
And this is finally how I use it, for example:
// filtering example (not important for this matter):
var filtering = {author: "Daniel"};
var contents = [];
var resource = QuestionFactory;
resource.get(filtering, function (res) {
// success fetching
$scope.contents = $scope.contents.concat(res['objects']);
}
// failed fetching
, function (err) {
}
);
The requests
first request:
question?format=json&limit=12&offset=0
second request and on:
question?format=json&limit=0&offset=0
My problem was that the DEFAULT_PARAMS variable was declared as global. I didn't realize that invoking the secured factory with {limit: 0} will override the global, therefore changing the limit to 0 for ALL of my resources.
Changing the securedFactory to a service and moving the "globals" into the returned function solved it. Had to add new ofcourse before every securedService call.

Resources