I am trying to figure out how to parse the JSON data that I am getting from the Spotify API. I am using this node module https://www.npmjs.com/package/spotify-web-api-js to get Spotify playlist tracks.
I am using this to GET my json (see what I did there)
export class HomePage {
spotifyApi = new SpotifyWebApi;
constructor() {}
}
var spotifyApi = new SpotifyWebApi();
spotifyApi.setAccessToken('Spotify OAuth Token');
spotifyApi.getPlaylistTracks('37i9dQZEVXbMDoHDwVN2tF')
.then(function(data) {
console.log('Playlist Tracks', data);
}, function(err) {
console.error(err);
var prev = null;
function onUserInput(queryTerm) {
// abort previous request, if any
if (prev !== null) {
prev.abort();
}
// store the current promise in case we need to abort it
prev = spotifyApi.searchTracks(queryTerm, {limit: 5});
prev.then(function(data) {
// clean the promise so it doesn't call abort
prev = null;
// ...render list of search results...
}, function(err) {
console.error(err);
});
}
This returns a JSON file but for some reason (probably my mistake) when I use JSON.parse(data);
console.log(data.name) it doesn't work (I know I am doing something wrong here but I don't know how to fix it). Thanks in advance :{)
If you want to get the tracks from the url you have to do this data.tracks.track[0] replace 0 with the needed tracks.
Related
I'm running into issues with trying to convert a json response from an Api call into an interface that will be accepted by this buildFileTree. So the call is pulling from SQL, it is working in dapper, I also see the array of data in my webapp in my console. The issue is when I try to change the initialize() value for buildFileTree from my static json file 'SampleJson' (inside the project) to my new interface 'VehicleCatalogMod' the tree shows up with SampleJson but when I switch the data to VehicleCatalogMod, the tree collapses.
dataStoreNew: VehicleCatalogMod[] = [];
constructor(private _servicesService: ServicesService){
this._servicesService.GetVehicleCat()
.subscribe(data => {
this.dataStoreNew = [];
this.dataStoreNew = data;
console.log(data);
})
this.initialize();
}
initialize() {
this.treeData = SampleJson;
// Working as SampleJson this is where the problem happens
const data = this.buildFileTree(VehicleCatalogMod, 0);
console.log(data);
this.dataChange.next(data);
}
buildFileTree(obj: object, level: number): TodoItemNode[] {
return Object.keys(obj).reduce<TodoItemNode[]>((accumulator, key) => {
let value = obj[key];
const node = new TodoItemNode();
node.item = key;
if (value != null) {
if (typeof value === 'object') {
node.children = this.buildFileTree(value, level + 1);
} else {
node.item = value;
}
}
return accumulator.concat(node);
}, []);
}
GetVehicleCat(): Observable<any> {
console.log('Vehicle Catalog got called');
return this.http.get('https://api/right/here',
{ headers: this.options.headers });
}
I tried a multitude of different things to try & get this working. I'm pretty much stagnated. Same error occurs when I try this.dataStoreNew instead. No errors in console, it literally just collapses the tree into one non distinguishable line. Also when I used: const vcm = new VehicleCatalogMod(); it made the tree pop up with the different properties but not the API values.
I also attached an image of the HTML element that appears.
with VehicleCatalogMod
with SampleJson
when i am trying to get this console.log(this.empresas[0]); it says it is undefined, but empresas is loaded in the function.
empresas: any;
constructor(...) {
this.getEmpresas();
console.log(this.empresas[0]);
}
getEmpresas(){
this.empresas = [];
this.http.get("http://url").subscribe( data => {
this.empresas = JSON.parse(data["_body"]);
}, err =>{
console.log(err);
});
}
Because just by calling getEmprass(), the value of this.emprass will not get updated. getEmprass() will immediately return without updating it. Later on, when the http request is completed, this.emprass gets updated, but that is after the time you printed its value.
Many things happen in Javascript asynchronously. Observables and subscribe function is one of them: its content gets executed asynchronously in the future (similar to callbacks).
You may wanna change your code to this:
constructor(...) {
this.getEmpresas();
console.log('hey, getEmpresas() finished!');
}
getEmpresas(){
this.empresas = [];
this.http.get("http://url").subscribe( data => {
this.empresas = JSON.parse(data["_body"]);
console.log(this.empresas[0]);//---------------------------> Print it here
}, err =>{
console.log(err);
});
}
If you want to wait in constructor function until the http request is finished, you should use an async function .
I am creating a website that reads externally hosted json files and then uses node.js to populate the sites content.
Just to demonstrate what I'm after, this is a really simplified version of what I'm trying to do in node.js
var ids = [111, 222, 333];
ids.forEach(function(id){
var json = getJSONsomehow('http://www.website.com/'+id+'.json');
buildPageContent(json);
});
Is what I want to do possible?
(Marked as a duplicate of "How do I return the response from an asynchronous call?" see my comment below for my rebuttal)
You are trying to get it synchronously. What you should aim for instead, is not a function used like this:
var json = getJSONsomehow('http://www.website.com/'+id+'.json');
but more like this:
getJSONsomehow('http://www.website.com/'+id+'.json', function (err, json) {
if (err) {
// error
} else {
// your json can be used here
}
});
or like this:
getJSONsomehow('http://www.website.com/'+id+'.json')
.then(function (json) {
// you can use your json here
})
.catch(function (err) {
// error
});
You can use the request module to get your data with something like this:
var request = require('request');
var url = 'http://www.website.com/'+id+'.json';
request.get({url: url, json: true}, (err, res, data) => {
if (err) {
// handle error
} else if (res.statusCode === 200) {
// you can use data here - already parsed as json
} else {
// response other than 200 OK
}
});
For a working example see this answer.
For more info see: https://www.npmjs.com/package/request
I think problem is in async request. Function will return result before request finished.
AJAX_req.open( "GET", url, true );
Third parameter specified async request.
You should add handler and do all you want after request finished.
For example:
function AJAX_JSON_Req( url ) {
var AJAX_req = new XMLHttpRequest.XMLHttpRequest();
AJAX_req.open( "GET", url, true );
AJAX_req.setRequestHeader("Content-type", "application/json");
AJAX_req.onreadystatechange = function() {
if (AJAX_req.readyState == 4 && AJAX_req.status == 200) {
console.log(AJAX_req.responseText);
}
};
}
I'm using NodeJS and an npm package called oauth to communicate with Twitter's search API. For some reason however, twitter is returning to me an empty array of statuses without any error... What is even more confusing is the fact that using a tool like Postman with the exact same request and keys returns the list of tweets? It makes no sense! Here is my request:
URL: https://api.twitter.com/1.1/search/tweets.json?count=100&q=hello&since_id=577103514154893312&max_id=577103544903462913
Here is my code:
var twitter_auth = new OAuth(
"https://api.twitter.com/oauth/request_token",
"https://api.twitter.com/oauth/access_token",
config.consumer_key,
config.consumer_secret,
"1.0A",
null,
"HMAC-SHA1"
);
var request = twitter_auth.get(
"https://api.twitter.com/1.1/search/tweets.json" + url,
config.access_token,
config.access_token_secret
);
var chunk = "", message = "", that = this;
request.on("response", function(response){
response.setEncoding("utf8");
response.on("data", function(data){
chunk += data;
try {
message = JSON.parse(chunk);
} catch(e) {
return;
}
console.log(message);
if(message.statuses)
{
for(var i = 0; i < message.statuses.length; i++)
{
var tweet = message.statuses[i];
that.termData[term.name].push(tweet);
}
if(message.search_metadata.next_results)
{
that.openRequests.push(that.createNewSearch(message.search_metadata.next_results, term));
}
else
{
that.termCompleted(term);
}
}
else if(message)
{
console.log("Response does not appear to be valid.");
}
});
response.on("end", function(){
console.log("Search API End");
});
response.on("error", function(err){
console.log("Search API Error", err);
});
});
request.end();
The console.log(message) is returning this:
{
statuses: [],
search_metadata: {
completed_in: 0.007,
max_id: 577103544903462900,
max_id_str: '577103544903462913',
query: 'hello',
refresh_url: '?since_id=577103544903462913&q=hello&include_entities=1',
count: 100,
since_id: 577103514154893300,
since_id_str: '577103514154893312'
}
}
Any ideas what is going on? Why is the statuses array empty in my code but full of tweets in Postman?
This issue was described at twittercommunity.com.
Accordingly answer of user rchoi(Twitter Staff):
"Regarding web vs. API search, we're aware that the two return different results at the moment. We made upgrades to the web search. There is no timeline for those
changes to be brought to other parts of our system."
Try to use
https://dev.twitter.com/rest/reference/get/statuses/mentions_timeline
https://dev.twitter.com/rest/reference/get/statuses/user_timeline
if you get empty result with api search functionality.
Please follow this link
https://twittercommunity.com/t/search-tweets-api-returned-empty-statuses-result-for-some-queries/12257/6
I'm pretty new to Angular so maybe I'm asking the impossible but anyway, here is my challenge.
As our server cannot paginate JSON data I would like to stream the JSON and add it page by page to the controller's model. The user doesn't have to wait for the entire stream to load so I refresh the view fo every X (pagesize) records.
I found oboe.js for parsing the JSON stream and added it using bower to my project. (bower install oboe --save).
I want to update the controllers model during the streaming. I did not use the $q implementation of pomises, because there is only one .resolve(...) possible and I want multiple pages of data loaded via the stream so the $digest needs to be called with every page. The restful service that is called is /service/tasks/search
I created a factory with a search function which I call from within the controller:
'use strict';
angular.module('myStreamingApp')
.factory('Stream', function() {
return {
search: function(schema, scope) {
var loaded = 0;
var pagesize = 100;
// JSON streaming parser oboe.js
oboe({
url: '/service/' + schema + '/search'
})
// process every node which has a schema
.node('{schema}', function(rec) {
// push the record to the model data
scope.data.push(rec);
loaded++;
// if there is another page received then refresh the view
if (loaded % pagesize === 0) {
scope.$digest();
}
})
.fail(function(err) {
console.log('streaming error' + err.thrown ? (err.thrown.message):'');
})
.done(function() {
scope.$digest();
});
}
};
});
My controller:
'use strict';
angular.module('myStreamingApp')
.controller('MyCtrl', function($scope, Stream) {
$scope.data = [];
Stream.search('tasks', $scope);
});
It all seams to work. After a while however the system gets slow and the http call doesn't terminate after refreshing the browser. Also the browser (chrome) crashes when there are too many records loaded.
Maybe I'm on the wrong track because passing the scope to the factory search function doesn't "feel" right and I suspect that calling the $digest on that scope is giving me trouble. Any ideas on this subject are welcome. Especially if you have an idea on implementing it where the factory (or service) could return a promise and I could use
$scope.data = Stream.search('tasks');
in the controller.
I digged in a little further and came up with the following solution. It might help someone:
The factory (named Stream) has a search function which is passed parameters for the Ajax request and a callback function. The callback is being called for every page of data loaded by the stream. The callback function is called via a deferred.promise so the scope can be update automatically with every page. To access the search function I use a service (named Search) which initially returns an empty aray of data. As the stream progresses the factory calls the callback function passed by the service and the page is added to the data.
I now can call the Search service form within a controller and assign the return value to the scopes data array.
The service and the factory:
'use strict';
angular.module('myStreamingApp')
.service('Search', function(Stream) {
return function(params) {
// initialize the data
var data = [];
// add the data page by page using a stream
Stream.search(params, function(page) {
// a page of records is received.
// add each record to the data
_.each(page, function(record) {
data.push(record);
});
});
return data;
};
})
.factory('Stream', function($q) {
return {
// the search function calls the oboe module to get the JSON data in a stream
search: function(params, callback) {
// the defer will be resolved immediately
var defer = $q.defer();
var promise = defer.promise;
// counter for the received records
var counter = 0;
// I use an arbitrary page size.
var pagesize = 100;
// initialize the page of records
var page = [];
// call the oboe unction to start the stream
oboe({
url: '/api/' + params.schema + '/search',
method: 'GET'
})
// once the stream starts we can resolve the defer.
.start(function() {
defer.resolve();
})
// for every node containing an _id
.node('{_id}', function(node) {
// we push the node to the page
page.push(node);
counter++;
// if the pagesize is reached return the page using the promise
if (counter % pagesize === 0) {
promise.then(callback(page));
// initialize the page
page = [];
}
})
.done(function() {
// when the stream is done make surethe last page of nodes is returned
promise.then(callback(page));
});
return promise;
}
};
});
Now I can call the service from within a controller and assign the response of the service to the scope:
$scope.mydata = Search({schema: 'tasks'});
Update august 30, 2014
I have created an angular-oboe module with the above solution a little bit more structured.
https://github.com/RonB/angular-oboe