AngularJS JSONP not updating key value to object - javascript

I was trying to update the channel.status object with the //Second JSONP callback function.
angular.element(document).ready(function() {
channels = [];
channel = {};
followersAPI = followersAPI.replace(/theUser/, user);
followersAPI = $sce.trustAsResourceUrl(followersAPI);
// First JSONP
$http.jsonp(followersAPI).then(function (response) {
var follows = response.data.follows;
for (var i = 0; i < follows.length; i ++) {
var currentChannel = follows[i].channel;
if (currentChannel.status == null) {
currentChannel.status = "offline";
}
channel = {
name: currentChannel.name,
display_name: currentChannel.display_name,
logo: currentChannel.logo,
url: currentChannel.url,
status: "loading"
};
streamAPI = "https://wind-bow.glitch.me/twitch-api/streams/";
streamAPI += channel.name;
streamAPI = $sce.trustAsResourceUrl(streamAPI);
// Second JSONP
$http.jsonp(streamAPI).then(function (response) {
data = response.data;
if (data.stream == null) {
channel["status"] = "offline";
} else {
channel["status"] = data.stream.channel.status;
}
});
channels.push(channel);
}
});
$scope.channels = channels;
console.log($scope.channels);
});
There's no error message, but only the last channel{} object in channels[] array was updated.
Here's the HTML part for channel.status:
<div id="channel-status" class="col-md-6">
<a
class="btn"
href="{{channel.url}}"
target="_blank">
{{channel.status}}
</a>
</div>

For asynchronous call $http.jsonp in for-loop, in its callback you will only get instance of last defined channel.as a solution, you should move all things about channel into the Second JSONP.
var currentChannel = follows[i].channel;
streamAPI = "https://wind-bow.glitch.me/twitch-api/streams/";
streamAPI += channel.name;
streamAPI = $sce.trustAsResourceUrl(streamAPI);
// Second JSONP
$http.jsonp(streamAPI).then(function (response) {
data = response.data;
if (currentChannel.status == null) {
currentChannel.status = "offline";
}
channel = {
name: currentChannel.name,
display_name: currentChannel.display_name,
logo: currentChannel.logo,
url: currentChannel.url,
status: "loading"
};
if (data.stream == null) {
channel["status"] = "offline";
} else {
channel["status"] = data.stream.channel.status;
}
channels.push(channel);
});

Related

Async call in javascript For Loop not working

I have a callback function inside a loop here for (var res in results) {
but it seems the loop is not waiting for the async call. When I am calling self.callTestOutputData(test_output_url) here, the loop is not waiting fpor the response but continuing for the next iteration and I am losing out the value to push into obj.requistion_number = testOutputResponse.value;
Please note : var results = response.results Here results is an array of Json objects.
Edit 1 : I tried forEach but that didn't work .
results.forEach(res => {
var obj = {}
obj.ferp = res.name;
// your code...
})
Original Code:
self.downloadDailyExcelProcurement = function (filters, excelTmpArr) {
self.disableExcelDownloadProcurement(true);
$('.useCaseExcelButtonProcurement .oj-button-button .oj-button-label')[0].style.backgroundColor = "gray";
$('.useCaseExcelButtonProcurement .oj-button-button .oj-button-label .demo-download-icon-24')[0].style.color = "#D8D8D8";
var payload = {};
if (typeof filters === "string") {
var fill = filters;
} else {
var fill = self.sendFilters();
if(self.app() === "fusion"){
fill += '&module=Procurement';
}else if (self.app() === "o2r"){
fill += '&module=O2r';
}
}
if(fill.includes("%3A")){
fill = fill.replace(/%3A/g, ':');
}
payload.Endpoint = 'executions/testcollection/' + fill;
//console.log(payload.Endpoint)
payload.BeforeSend = function (xhr) {
xhr.setRequestHeader('Authorization', 'Basic ' + btoa('guest:oracle123'));
$(".custom-loader-circle").show();
};
payload.OnSuccess = function (response) {
var results = response.results;
for (var res in results) {
var obj = {}
obj.ferp = results[res].name;
obj.po = "NA"
obj.receipt_no = "NA"
var test_output_url = results[res].reference_test_cases[0].automation_tests[0].test_outputs[0]
$.when(self.callTestOutputData(test_output_url)).done(function (testOutputResponse) {
if(testOutputResponse)
obj.requistion_number = testOutputResponse.value;
else {
obj.requistion_number = "NA";
}
self.excelTmpArr().push(obj);
});
}
else {
self.excelTmpArr().push(obj);
}
}
if (response.next) {
filters = ((response.next).split('testcollection'))[1];
if (filters[0] === "/") {
var test = filters.slice(1, filters.length);
}
self.downloadDailyExcelProcurement(test, self.excelTmpArr());
} else {
if (results.length === 0) {
$(".custom-loader-circle").hide();
self.disableExcelDownloadProcurement(false);
$('.useCaseExcelButtonProcurement .oj-button-button .oj-button-label')[0].style.backgroundColor = "#4d0000";
$('.useCaseExcelButtonProcurement .oj-button-button .oj-button-label .demo-download-icon-24')[0].style.color = "white";
showMessage(self.messages, "No Data to Download", '', 'info');
} else {
self.formatForExcel(self.excelTmpArr(), fill, "Procurement");
}
}
};
payload.OnError = function (data) {
showMessage(self.messages, data.status, data.statusText, 'error');
$(".custom-loader-circle").hide();
};
getData(payload);
}
Try using async and await :
async function asyncCall () {
// call here
}
for (var res in results) {
const response = await asyncCall();
}
var results = response.results;
if(result.length > 0){
results.map((data,index)=>{
//write your code here
})
}
This will help you ..
Use forEach() to iterate since it creates its own function closure:
results.forEach(res => {
var obj = {}
obj.ferp = res.name;
// your code...
})

How to filter records of only changed rows in a table and send them to API using angular table and C#?

currently after saving data in my table, I am sending complete users list to API. But my requirement is to send only changed records based on context like absent/ present to API(not all users list). How to achieve above functionality?
var request = {};
$scope.noUnsavedData = true;
for (var attIndex in $scope.Attendance) {
if ($scope.Attendance[attIndex].IsMorningAbsent != $scope.savedAttendance[attIndex].IsMorningAbsent ||
$scope.Attendance[attIndex].IsAfternoonAbsent != $scope.savedAttendance[attIndex].IsAfternoonAbsent) {
$scope.noUnsavedData = false;
break;
}
}
//send sms
request["academicYearId"] = AcademicYear.getYear();
request["date"] = $filter('date')($scope.Date.dt, 'yyyyMMdd');
var userList = "";
for (var i = 0; i < $scope.model.length; i++) {
if (i == 0) {
userList = $scope.model[i].StaffId;
} else {
userList += "," + $scope.model[i].StaffId;
}
}
request["userList"] = userList;
//academicyearid, date
if ($scope.noUnsavedData) {
if (Status === "Absent") {
StaffService.resource.apiAbsentName(request, function (response) {
alert(response.Message);
});
} else if (Status === "Present") {
StaffService.resource.apiPresentName(request, function (response) {
alert(response.Message);
});
}
}

Vue - infinite scroll doesn't fetch new data when bottom of the page is reached

So, I made a method fetchStories that fetches stories from my API in batches of 10, and the new stories can be fetched once you've scrolled to the bottom of the screen. This is what part of my Vue instance looks like:
var discoveryFeed = new Vue({ // eslint-disable-line no-unused-vars
el: '#discoveryFeed',
data: {
userId: userId,
username: username,
tags: [],
selectedTag: null,
stories: [],
checklist: [],
limit: 10,
page: 1,
isEnd: false,
busy: false,
isFollowing: true
},
beforeMount: function() {
var self = this;
self.$http.get('/api/tags')
.then(function(res) {
self.tags = res.body;
}, function(err) {
self.tags = [];
});
self.$http.get('/api/user/' + self.username + '/checklist')
.then(function(res) {
self.checklist = res.body || [];
}, function(err) {
self.checklist = [];
});
self.fetchStories();
},
methods: {
fetchStories: function(isNew) {
var self = this;
isNew = Boolean(isNew);
if(self.isEnd) { return; }
self.busy = true;
var url = '/api/discover';
if(self.selectedTag) {
url = `/api/tags/${self.selectedTag.code}/stories`;
}
url += '?p=' + self.page;
url += '&l=' + self.limit;
self.page += 1;
self.$http.get(url)
.then(function(res) {
self.busy = false;
if(res.body.length === 0) {
self.isEnd = true;
return;
}
if(isNew) {
self.stories = [];
}
self.stories = self.stories.concat(res.body);
}, function(err) {
self.busy = false;
self.stories = [];
});
},
setTag: function(tag) {
var self = this;
self.selectedTag = tag;
for(var i = 0; i < self.tags.length; i++) {
self.tags[i].selected = false;
}
self.selectedTag.selected = true;
self.page = 1;
self.fetchStories(true);
}
In my pug, I'm using the v-infinite-scroll directive to call the method fetchStories. Also note that I'm working with a list of tags, and clicking a new tag will load different sets of stories through the method setTag(tag).
nav.col-lg-3.col-md-4.d-none.d-md-block.d-lg-block.bg-sidebar(v-cloak)
.feed-sidebar.feed-sidebar-interests.border-top-0.border-bottom-0.border-left-0.position-sticky
strong Your Interests
div.list-group.list-unstyled.mt-2(v-if="tags.length > 0")
a.tag-btn.mb-2.align-middle.text-black(v-for="tag, index in tags" v-bind:class="{ active: selectedTag && selectedTag.id === tag.id }" #click="setTag(tag); scrollToTop();")
i.fa-fw.mr-2(v-bind:class="tag.icon + ' fa-lg'"
v-bind:style="'color:' + tag.hexColor")
span {{ tag.name }}
.col-lg-6.col-md-8(v-cloak
v-infinite-scroll="fetchStories"
infinite-scroll-disabled="busy"
infinite-scroll-distance="50")
Upon checking the data response at the initial load, the ten stories are fetched at /stories?p=1&l=10. However, upon reaching the bottom the data response array of /stories?p=2&l=10 is empty. This may have something to do with my use of booleans to set flags when choosing a tag.
Apparently, I should have reset the value of isEnd when I'm setting the new tag.
setTag: function(tag) {
var self = this;
self.selectedTag = tag;
for(var i = 0; i < self.tags.length; i++) {
self.tags[i].selected = false;
}
self.selectedTag.selected = true;
self.page = 1;
self.isEnd = false;
self.fetchStories(true);
}

How can Actionhero receive the parameter without inputs defined?

I'm using ActionHero in node.js and Angular.js.
I am trying images send to ActionHero using $http method.
but I don't know How many images are made.
so I can't define the parameter names on action in ActionHero.
below is my source.
First. images are in object, so I change object to each parameter.
insert: function (param, next) {
var url = settings.apiUrl + "/api/online/productAdd";
var vdata = {
img_objects :param.img_objects
};
angular.forEach(param.img_objects, function (v, k) {
vdata['img_file'+(k)] = v.files;
});
commonSVC.sendUrlFile("POST", url, vdata, function (state, data) {
next(state, data);
});
}
Second. make formData in sendUrlFile like source below. and then send to actionHero.
var promise = $http({
method: method,
url: url,
headers: {
'Content-Type': undefined
},
data: params,
transformRequest: function (data) {
var formData = new FormData();
angular.forEach(data, function (value, key) {
if(angular.isObject(value)){
if(value.lastModified > 0 && value.size > 0){
formData.append(key, value);
}else{
formData.append(key, JSON.stringify(value));
}
}else{
formData.append(key, value);
}
});
return formData;
}
});
Third. ActionHero is received. but parameter isn't defined so ActionHero can't receive.
exports.productAdd = {
name: 'online/productAdd',
inputs: {
I don't know How Many Images are made? 1~10? or 1~100?
},
authenticate: true,
outputExample: {
'result':'success'
}
So I have two Questions:
How can actionhero receive the parameter without inputs defined?
Can I object with Image Data send to ActionHero by Ajax?
Thank You.
I change reduceParams function in actionProcessor.js.
api.actionProcessor.prototype.reduceParams = function(){
var self = this;
var inputNames = [];
if(self.actionTemplate.inputs){
inputNames = Object.keys(self.actionTemplate.inputs);
}
// inputs * 확인 2017-01-20 Eddy
var multi = [];
var strArray;
for(var v in inputNames){
if(inputNames[v].indexOf("*") != -1){
strArray = inputNames[v].split('*');
multi.push(strArray[0]);
}
}
var multiLength = multi.length;
var flag;
if(api.config.general.disableParamScrubbing !== true){
for(var p in self.params){
flag = true;
if(multiLength > 0){
for(var i=0; i<multiLength; i++){
if(p.indexOf(multi[i]) != -1){
flag = false;
}
}
}
if(flag){
if(api.params.globalSafeParams.indexOf(p) < 0 && inputNames.indexOf(p) < 0){
delete self.params[p];
}
}
}
}
};
i can define on inputs like below.
'img_*' : {required: false}
and Then I make middleware
var actionHeroMiddleware = {
name: '-',
global: true,
priority: 1000,
preProcessor: function(data, next) {
api.actionProcessor.prototype.reduceParams = function(){
var self = this;
var inputNames = [];
if(self.actionTemplate.inputs){
inputNames = Object.keys(self.actionTemplate.inputs);
}
// inputs * 확인 2017-01-20 Eddy
var multi = [];
var strArray;
for(var v in inputNames){
if(inputNames[v].indexOf("*") != -1){
strArray = inputNames[v].split('*');
multi.push(strArray[0]);
}
}
var multiLength = multi.length;
var flag;
if(api.config.general.disableParamScrubbing !== true){
for(var p in self.params){
flag = true;
if(multiLength > 0){
for(var i=0; i<multiLength; i++){
if(p.indexOf(multi[i]) != -1){
flag = false;
}
}
}
if(flag){
if(api.params.globalSafeParams.indexOf(p) < 0 && inputNames.indexOf(p) < 0){
delete self.params[p];
}
}
}
}
};
next();
},
stop: function(api, next) {
next();
}
};
api.actions.addMiddleware(actionHeroMiddleware);
next();

Trouble using nested callbacks in NodeJS

I'm writing a program that scrapes a site for links, then scrapes these links for information. In order to scrape the site, it is necessary to log in first. And so the order is: Log in -> Scrape the index for links -> Scrape the links for info
The callback to the login function prints an empty array { results: [], hasMore: true }, so something is wrong with my code (the scraping part works):
var request = require('request');
var request = request.defaults({jar: true}); // necessary for persistent login
var cheerio = require('cheerio');
var url1 = "https://example.org/torrents/browse/index/";
var loginUrl = "https://example.org/user/account/login/";
var credentials = {
username: 'user1',
password: 'passpass'
};
login(function (result) {
console.log(result);
});
function login(callback) {
request.post({
uri: loginUrl,
headers: { 'content-type': 'application/x-www-form-urlencoded' },
body: require('querystring').stringify(credentials)
}, function(err, res, body){
if(err) {
console.log("Login error");
return;
}
scrapeTorrents(url1, function (result) {
callback(result);
});
});
}
function scrapeTorrents(url, callback) {
request(url, function(err, res, body) {
if(err) {
console.log("Main scrape error");
return;
}
var links = []
var $ = cheerio.load(body);
$('span.title').each(function(i, element){
var title = $(this);
var a = $(this).children().eq(0);
var detailsUrl = a.attr('href');
//console.log(detailsUrl);
links.push(detailsUrl);
});
scrapeTorrentDetails(links, function (result) {
callback(result);
});
});
}
function scrapeTorrentDetails(links, callback) {
var results = [];
function getDetails(url) {
request(url, function(err, res, body) {
if(err) {
console.log("Detail scrape error");
return;
}
console.log("Scraping: " + url);
var $ = cheerio.load(body);
var tds = $('td');
var title = $(tds).get(1).firstChild.data;
var hash = $(tds).get(3).firstChild.data.trim();
var size = $(tds).get(9).firstChild.data;
// console.log(tds.length);
if (tds.length > 23) {
var rlsDate = $(tds).get(23).firstChild.data || '';;
var genres = $(tds).get(27).firstChild.data || '';;
var runtime = $(tds).get(31).firstChild.data || '';;
if ( $(tds).get(33).firstChild != null) {
var plot = $(tds).get(33).firstChild.data || '';;
}
var rating = $('#imdb_rating').parent().next().text() || '';; // of 10
var imdb_id = $('[name=imdbID]').get(0).attribs.value || '';;
var cover = $('#cover').children().eq(0).get(0).attribs.href || '';;
var thumb = $('[alt=Cover]').get(0).attribs.src || '';;
if (typeof cover == 'undefined') {
cover = thumb;
}
} else {
var rlsDate = "notfound";
var genres = "notfound";
var runtime = "notfound";
var plot = "notfound";
var rating = "notfound"; // of 10
var imdb_id = "notfound";
var cover = "notfound";
var thumb = "notfound";
}
var movie = {
type: 'movie',
imdb_id: imdb_id,
title: title,
year: rlsDate,
genre: genres,
rating: rating,
runtime: runtime,
image: thumb,
cover: cover,
synopsis: plot,
torrents: {
magnet: 'magnet:?xt=urn:btih:' + hash + '&tr=http://tracker.example.org:2710/a/announce',
filesize: size
}
};
results.push(movie);
});
}
for (var i=0; i<links.length; i++){
getDetails("https://example.org" + links[i]);
}
callback( {
results: results,
hasMore: true
});
}
Maybe Q promises would be better. How would I implement that in the code above?
If you're wondering what the code is for, I'm planning to modify Popcorn-time to use another torrent-tracker (without an API).
Thanks
A main problem is with this code:
for (var i=0; i<links.length; i++){
getDetails("https://example.org" + links[i]);
}
callback( {
results: results,
hasMore: true
});
getDetails() is async, but you just call it links.length times and move on - acting like they have all completed. So, none of the requests in getDetails() is done before you call the callback and try to pass the results. But, none of the results have yet been filled in so they will be empty.
You have all these other nested callbacks everywhere through your code (as required), yet you dropped the ball in this one place. You need to know when all the getDetails() calls are done before you call the final callback with the results.
In addition, you also have to decide if you're OK calling all the getDetails() calls in parallel (all in flight at once) or if what you really want to do is to call one, wait for it to finish, then call the next, etc... Right now you are putting them all in-flight at once which can work if the destination server doesn't object to that many requests all at once.
There are several potential strategies for fixing this.
Add a callback to getDetails() and then keep a count of when you've gotten links.length callbacks from getDetails() and only when the entire count has finished so you call the final callback.
Change getDetails() to return a promise. You can then use something like links.map(getDetails) to create an array of promises that you can then use Promise.all() with to know when they are all done.
Personally, I would change all of your code to use promises and I'd use the Bluebird promises library for it's extra features such as Promise.map() to make this even simpler.
Here's a fix that adds a callback to getDetails() and then counts how many are done:
function scrapeTorrentDetails(links, callback) {
var results = [];
function getDetails(url, done) {
request(url, function(err, res, body) {
if(err) {
console.log("Detail scrape error");
done(err);
return;
}
console.log("Scraping: " + url);
var $ = cheerio.load(body);
var tds = $('td');
var title = $(tds).get(1).firstChild.data;
var hash = $(tds).get(3).firstChild.data.trim();
var size = $(tds).get(9).firstChild.data;
// console.log(tds.length);
if (tds.length > 23) {
var rlsDate = $(tds).get(23).firstChild.data || '';;
var genres = $(tds).get(27).firstChild.data || '';;
var runtime = $(tds).get(31).firstChild.data || '';;
if ( $(tds).get(33).firstChild != null) {
var plot = $(tds).get(33).firstChild.data || '';;
}
var rating = $('#imdb_rating').parent().next().text() || '';; // of 10
var imdb_id = $('[name=imdbID]').get(0).attribs.value || '';;
var cover = $('#cover').children().eq(0).get(0).attribs.href || '';;
var thumb = $('[alt=Cover]').get(0).attribs.src || '';;
if (typeof cover == 'undefined') {
cover = thumb;
}
} else {
var rlsDate = "notfound";
var genres = "notfound";
var runtime = "notfound";
var plot = "notfound";
var rating = "notfound"; // of 10
var imdb_id = "notfound";
var cover = "notfound";
var thumb = "notfound";
}
var movie = {
type: 'movie',
imdb_id: imdb_id,
title: title,
year: rlsDate,
genre: genres,
rating: rating,
runtime: runtime,
image: thumb,
cover: cover,
synopsis: plot,
torrents: {
magnet: 'magnet:?xt=urn:btih:' + hash + '&tr=http://tracker.example.org:2710/a/announce',
filesize: size
}
};
results.push(movie);
done();
});
}
var doneCnt = 0;
for (var i=0; i<links.length; i++){
getDetails("https://example.org" + links[i], function() {
++doneCnt;
if (doneCnt === links.length) {
callback( {
results: results,
hasMore: true
});
}
});
}
}
The following is the given sample code rewritten to use bind, a custom this object and a count of the requests that have yet to complete (I think promises obscure the execution path).
The reason that the callback is returning an empty array seems to be that there are no spans in the document with a title attribute, so as a result no further requests are triggered.
var
request = require('request').defaults({
jar: true
}), // necessary for persistent login
cheerio = require('cheerio'),
process = require('process'),
url1 = "https://example.org/torrents/browse/index/",
loginUrl = "https://example.org/user/account/login/",
login = function(callback) {
request.post({
uri: loginUrl,
headers: {
'content-type': 'application/x-www-form-urlencoded'
},
body: require('querystring').stringify({
username: 'user1',
password: 'passpass'
})
}, fna.bind({
callback: callback
}));
},
fna = function(err, res, body) {
if (err) {
console.log("Login error");
return;
}
request(url1, fnb.bind(this));
},
fnb = function(err, res, body) {
if (err) {
console.log("Main scrape error");
return;
}
var
$ = cheerio.load(body),
links = [],
fnd = fne.bind(this);
$('span.title').each(function() {
links.push($(this).children().first().attr('href'));
});
this.results = [];
this.resultCount = links.length;
if (this.resultCount) {
fnd = fnc.bind(this);
for (var i = 0; i < links.length; i++) {
request("https://example.org" + links[i], fnd);
}
} else {
process.nextTick(fnd);
}
},
fnc = function(err, res, body) {
if (err) {
console.log("Detail scrape error");
return;
}
console.log("Scraping: " + url);
var
$ = cheerio.load(body),
tds = $('td'),
title = $(tds).get(1).firstChild.data,
hash = $(tds).get(3).firstChild.data.trim(),
size = $(tds).get(9).firstChild.data,
rlsDate = "notfound",
genres = "notfound",
runtime = "notfound",
plot = "notfound",
rating = "notfound", // of 10
imdb_id = "notfound",
cover = "notfound",
thumb = "notfound";
if (tds.length > 23) {
rlsDate = $(tds).get(23).firstChild.data || '';
genres = $(tds).get(27).firstChild.data || '';
runtime = $(tds).get(31).firstChild.data || '';
if ($(tds).get(33).firstChild != null) {
plot = $(tds).get(33).firstChild.data || '';
}
rating = $('#imdb_rating').parent().next().text() || ''; // of 10
imdb_id = $('[name=imdbID]').get(0).attribs.value || '';
cover = $('#cover').children().eq(0).get(0).attribs.href || '';
thumb = $('[alt=Cover]').get(0).attribs.src || '';
if (typeof cover == 'undefined') {
cover = thumb;
}
}
this.results.push({
type: 'movie',
imdb_id: imdb_id,
title: title,
year: rlsDate,
genre: genres,
rating: rating,
runtime: runtime,
image: thumb,
cover: cover,
synopsis: plot,
torrents: {
magnet: 'magnet:?xt=urn:btih:' + hash + '&tr=http://tracker.example.org:2710/a/announce',
filesize: size
}
});
this.resultCount--;
if (this.resultCount === 0) {
this.callback({
results: this.results,
hasMore: true
});
}
},
fne = function() {
this.callback({
results: this.results,
hasMore: true
});
};
login(function(result) {
console.log(result);
});

Categories