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
Related
I'm making http get call from angularJS function. Call works perfectly and get the response back but I need to store response in a variable so I can use it outside of $http. I tried to keep in $scope.data.submissionFileID but alert($scope.data.submissionFileID) says undefined. Also I want me make this call synchronous. I'm new to this, can you please help to modify this below code?
$scope.openWindow = function (e) {
var url = '/Submission/GetSubmissionFileInfo?' + 'id=' + SubmissionID;
$http.get(url).success(function (response) {
$scope.data.submissionFileID = response; // response is an integer such as 123
});
alert($scope.data.submissionFileID); // This is undefined, what should I do to fix it?
var content = "<h7><b>" + "Created at </b>" + $scope.data.submissionFileID + "</h7><br><br>";
}
Something to consider is that the alert(...) is being called before the async function has a chance to complete. An option would be to send the response off to another function that sets the $scope variable and does whatever else you might want.
$scope.openWindow = function (e) {
var url = '/Submission/GetSubmissionFileInfo?' + 'id=' + SubmissionID;
$http.get(url).success(function (response) {
$scope.doTheThing(response);
});
}
$scope.data = {}
$scope.doTheThing = function(response) {
$scope.data.submissionFileID = response;
}
in the HTML template file...
<div ng-if="!data.submissionFileID">Retrieving Data....</div>
<div ng-if="data.submissionFileID">
<h7><b>Created at </b> {{data.submissionFileID}}</h7>
</div>
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);
}
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.
The API i am using returns header "Content-range" for lists :
Request : GET /users
Response contains : Content-Range: 0-49/1337
What is the correct way to generalize the retrieval (and parsing) of this header value with Restangular ?
Do i have to make my own function ?
Is there a existing method that can "catch" every response, check if the header is there and then append the parsed values (i.e : limit, range, total_count, page number) to Restangular returned object ?
Restangular has addresponseinterceptor for catching responses. You can catch your response there and get response.headers.
You should return a restangularized element in the interceptor otherwise you cannot get any data in your controllers or services...
So an example can be like this...
RestangularProvider.addResponseInterceptor(function (data, operation, what, url, response, deferred) {
var data = response.data;
var contentRange = response.headers("Content-Range");
if (contentRange) {
data.limit = contentRange.limit;
}
return data;
});
As you see you can add Restangular response interceptor on any angular config blog by injection RestangularProvider
After a few iterations on #wickY26 solution here is a technique that worked well for me :
Prerequisite
As mentioned in the question the API response contains this header :
Content-Range: 0-9/150
Which mean : The returned list will start at the 1st element (starting at 0) until the 9th on a total of 150.
On the other side the request has to include a "range=x-y" :
https://api.myapp.com/contents/?range=10-20
Code below will only work inside those contraints.
First step
Make Restangular grab all incoming responses from API. If we have a "Content-Range" header then append a pagination object with all important informations regarding pagination (from, to, total, numPage, currentPage) :
angular.module('myapp')
.config(dataConfigResponse);
dataConfigResponse.$inject = ['RestangularProvider'];
function dataConfigResponse(RestangularProvider) {
RestangularProvider.addResponseInterceptor(function (data, operation, what, url, response, deferred) {
var responseData = response.data;
var contentRange = response.headers('Content-Range');
if (contentRange) {
var rangeFields = contentRange.split(/\s|-|\//);
var paginationFrom = parseInt(rangeFields[0]) + 1;
var paginationTo = parseInt(rangeFields[1]) + 1;
var paginationTotal = parseInt(rangeFields[2]);
var paginationSubTotal = parseInt(paginationTo - paginationFrom);
responseData.pagination = {
from: paginationFrom,
to: paginationTo,
total: paginationTotal,
numPages: Math.ceil(paginationTotal / paginationSubTotal),
currentPage: Math.ceil(paginationFrom / paginationSubTotal)
};
}
return responseData;
});
}
Second step
Make Restangular intercept all outgoing requests to the API. When the action is a getList we append "?range=10-20" to the next query
angular.module('myapp')
.config(dataConfigRequest);
dataConfigRequest.$inject = ['RestangularProvider'];
function dataConfigRequest(RestangularProvider) {
RestangularProvider.addFullRequestInterceptor(function (element, operation, what, url, headers, params, httpConfig) {
if (operation === 'getList') {
if (Object.keys(params).length) {
params.range = ((params._page - 1) * params._perPage) + '-' + (params._page * params._perPage - 1);
delete params._page;
delete params._perPage;
}
}
});
}
Third step
Now the controller which is displaying the list.
function dashboardContentListCtrl($scope, dataService) {
// Pagination starting values
$scope.maxSize = 6;
$scope.currentPage = 1;
$scope.limit = 20;
$scope.pageChanged = function () {
$scope.params = {_page: $scope.currentPage, _perPage: $scope.limit};
dataService.getAllContents($scope.params).then(function (contents) {
$scope.pagination = contents.pagination;
$scope.contents = contents.plain();
}).catch(function () {
$scope.contents = [];
});
};
$scope.pageChanged();
On my app the pagination view component is angular-ui pagination directive, looking like this :
<pagination class="pagination-sm"
boundary-links="true"
ng-hide="pagination.total < limit"
total-items="pagination.total"
items-per-page="limit"
ng-model="currentPage"
max-size="maxSize"
rotate="false"
ng-change="pageChanged()"></pagination>
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.