using json data from localhost in js file using ruby - javascript

I'm creating a web app that uses MONGOHQ to store data, and that uses Sinatra to run the app. If I go to: localhost:4578/names.json, I get the names of all the names that I use for my data. However, I'm having trouble accessing this data using the getJSON method of jquery.
The file /names.json looks like this:
["Yasiel Puig","Nick Franklin","Mike Zunino","Jurickson Profar","Manny Machado"]
I tried doing something like this:
var series = []
$.get('names.json', function(n) {
n.forEach(function(s) {
series.push({
name: s
})
})
}, 'json')
But this does not really work. Do you have any other ideas for how I should access the json data? Could I save it to a var? I'm pretty sure the json data is not JSONP format, so maybe I should treat it as AJAX?

Your code seems to work, I tried it in this Fiddle. Therefore the problem is probably on server side.
var data = ["Yasiel Puig", "Nick Franklin", "Mike Zunino",
"Jurickson Profar", "Manny Machado"];
var series = [];
data.forEach( function( e ) {
series.push( {
name: e
});
}
);
series.forEach( function( e ) {
console.log( e.name );
});

there is a difference between calling $.get('names.json') and $.get('/names.json') I think you are not adding the starting slash(/) to the url
when you call $.get('names.json') it calls complete_current_url + '/names.json'
eg. if you are on /home page then the url that would be called is /home/names.json
and $.get('/names.json') will call current_domain + '/names.json'
from any page it will always call '/names.json'

Could I save it to a var?
Possibly. Though, it depends on which variable and where/when you need it.
$.get() is asynchronous. It only starts the request, sets the callback as a listener, and then exits.
var series = [];
$.get('names.json', function (n) {
n.forEach(function(s) {
series.push({
name: s
});
});
// inside the callback...
console.log(series); // filled: [ { name: "Yasiel Puig" }, ... ]
});
// after the request...
console.log(series); // still empty: []
So, you can use series, or more importantly n, within the callback. Outside, it won't be available yet.

Related

Unable to add async / await and then unable to export variable. Any help appreciated

Background: Been trying for the last 2 day to resolve this myself by looking at various examples from both this website and others and I'm still not getting it. Whenever I try adding callbacks or async/await I'm getting no where. I know this is where my problem is but I can't resolve it myself.
I'm not from a programming background :( Im sure its a quick fix for the average programmer, I am well below that level.
When I console.log(final) within the 'ready' block it works as it should, when I escape that block the output is 'undefined' if console.log(final) -or- Get req/server info, if I use console.log(ready)
const request = require('request');
const ready =
// I know 'request' is deprecated, but given my struggle with async/await (+ callbacks) in general, when I tried switching to axios I found it more confusing.
request({url: 'https://www.website.com', json: true}, function(err, res, returnedData) {
if (err) {
throw err;
}
var filter = returnedData.result.map(entry => entry.instrument_name);
var str = filter.toString();
var addToStr = str.split(",").map(function(a) { return `"trades.` + a + `.raw", `; }).join("");
var neater = addToStr.substr(0, addToStr.length-2);
var final = "[" + neater + "]";
// * * * Below works here but not outside this block* * *
// console.log(final);
});
// console.log(final);
// returns 'final is not defined'
console.log(ready);
// returns server info of GET req endpoint. This is as it is returning before actually returning the data. Not done as async.
module.exports = ready;
Below is an short example of the JSON that is returned by website.com. The actual call has 200+ 'result' objects.
What Im ultimately trying to achieve is
1) return all values of "instrument_name"
2) perform some manipulations (adding 'trades.' to the beginning of each value and '.raw' to the end of each value.
3) place these manipulations into an array.
["trades.BTC-26JUN20-8000-C.raw","trades.BTC-25SEP20-8000-C.raw"]
4) export/send this array to another file.
5) The array will be used as part of another request used in a websocket connection. The array cannot be hardcoded into this new request as the values of the array change daily.
{
"jsonrpc": "2.0",
"result": [
{
"kind": "option",
"is_active": true,
"instrument_name": "26JUN20-8000-C",
"expiration_timestamp": 1593158400000,
"creation_timestamp": 1575305837000,
"contract_size": 1,
},
{
"kind": "option",
"is_active": true,
"instrument_name": "25SEP20-8000-C",
"expiration_timestamp": 1601020800000,
"creation_timestamp": 1569484801000,
"contract_size": 1,
}
],
"usIn": 1591185090022084,
"usOut": 1591185090025382,
"usDiff": 3298,
"testnet": true
}
Looking your code we find two problems related to final and ready variables. The first one is that you're trying to console.log(final) out of its scope.
The second problem is that request doesn't immediately return the result of your API request. The reason is pretty simple, you're doing an asynchronous operation, and the result will only be returned by your callback. Your ready variable is just the reference to your request object.
I'm not sure about what is the context of your code and why you want to module.exports ready variable, but I suppose you want to export the result. If that's the case, I suggest you to return an async function which returns the response data instead of your request variable. This way you can control how to handle your response outside the module.
You can use the integrated fetch api instead of the deprecated request. I changed your code so that your component exports an asynchronous function called fetchData, which you can import somewhere and execute. It will return the result, updated with your logic:
module.exports = {
fetchData: async function fetchData() {
try {
const returnedData = await fetch({
url: "https://www.website.com/",
json: true
});
var ready = returnedData.result.map(entry => entry.instrument_name);
var str = filter.toString();
var addToStr = str
.split(",")
.map(function(a) {
return `"trades.` + a + `.raw", `;
})
.join("");
var neater = addToStr.substr(0, addToStr.length - 2);
return "[" + neater + "]";
} catch (error) {
console.error(error);
}
}
}
I hope this helps, otherwise please share more of your code. Much depends on where you want to display the fetched data. Also, how you take care of the loading and error states.
EDIT:
I can't get responses from this website, because you need an account as well as credentials for the api. Judging your code and your questions:
1) return all values of "instrument_name"
Your map function works:
var filter = returnedData.result.map(entry => entry.instrument_name);
2)perform some manipulations (adding 'trades.' to the beginning of each value and '.raw' to the end of each value.
3) place these manipulations into an array. ["trades.BTC-26JUN20-8000-C.raw","trades.BTC-25SEP20-8000-C.raw"]
This can be done using this function
const manipulatedData = filter.map(val => `trades.${val}.raw`);
You can now use manipulatedData in your next request. Being able to export this variable, depends on the component you use it in. To be honest, it sounds easier to me not to split this logic into two separate components - regarding the websocket -.

Promise in MagicSuggest data function

I have tried asking this question directly on github but there does not seem to be much movement in this project anymore. It would be great if someone on SO has an idea. Is it possible to return a promise in the data function? I have tried the following and it does not seem to work. The issue is that I am trying to make an ajax call within the data-function, which expects a result/data array. Of course I cannot do this when making an asynchronous ajax call.
var ms = $('#mycombo').magicSuggest({minChars: 2, data : function(q) {
return someAPI.findSuggestions(q, currentLang).then(function(response) {
if(!_.isEmpty(response.data.suggestions)) {
_.each(response.data.suggestions, function(suggestion) {
if (suggestion.id && suggestion.label) {
data.push({ id: suggestion.id, name: suggestion.label });
}
});
}
});
return data;
}});
If there is an alternative way of solving this, I would be very grateful for your help.
Thanks in advance.
Michael
For those interested, I have managed to find a solution to the problem. As posted on github (https://github.com/nicolasbize/magicsuggest/issues/281) you need to use the keyup event instead of setting the data property during initialization. So it now looks something like this:
var ms = $('#mycombo').magicSuggest({minChars: 2});
$(ms).on('keyup', function(e, m, v) {
// ... get data via ajax and call "ms.setData(data)" in the response callback ...
// ... you can use m.getRawValue() to get the current word being typed ...
ms.setData(data);
}
This will cause an ajax call to be fired after every key press, so you may want to improve this by adding some kind of a delay or something.
I've also done it this way:
const suggester: any = divElem.magicSuggest({
...more properties here...
data: (query) => {
if (query) {
this.myService.mySearch(query).take(1).subscribe((list) => {
suggester.setData(list);
});
}
return [];
},
...more properties here...
});
Where mySearch(query) returns:
Observable<MyObject[]>

Load data into local storage once?

I am using backbone.js I need a very simple way to render a local json file into the users local storage only one time. I am building a cordova app and I just want to work with local storage data.
I have hard coded a decent size .json file (list of players) into my collection, and I just want to load the .json file into the local storage if local storage on that device is empty which will only be once, upon initialization of the app.
I could use ajax, but I don't know how to write it to only inject data one time as "starter" data. So if you know how to do this I can upload the json file to my server and somehow fetch it.
I can inject the data if I go through a series of tasks, I have to disable the fetch method and render this code below in an each statement, plus the json has to be hardcoded into the collection, with a certain format.
playersCollection.create({
name: player.get('name'),
team: player.get('team'),
team_id: player.get('team_id'),
number: player.get('number'),
points: player.get('points')
})
I am trying to finish this lol I need to use it tonight to keep stats, I am almost there the structure works, when data is loaded I can add stats etc, but I need to get that data loaded, I pray someone can help!
Edit: I was able to put together some sloppy code last minuet that at least worked, thanks to #VLS I will have a much better solution, but Ill post the bad code I used.
// I fire renderData method on click
events: {
'click .renderData':'renderData'
},
// Inside my render method I check if "players-backbone" is in local storage
render: function() {
var self = this;
if (localStorage.getItem("players-backbone") === null) {
alert('yup null');
//playersCollection.fetch();
this.$el.append('<button class="renderData">Dont click</button>')
} else {
alert('isnt null');
this.$el.find('.renderData').remove();
playersCollection.fetch();
}
this.teams.each(function(team) {
var teamView = new TeamView({ model: team });
var teamHtml = teamView.render().el;
console.log($(''))
var teamPlayers = this.players.where({team_id: team.get('id')})
_.each(teamPlayers, function(player) {
var playerView = new PlayerView({ model: player });
var playerHtml = playerView.render().el;
$(teamHtml).append(playerHtml);
}, this);
this.$el.append(teamHtml);
}, this);
return this;
},
// method that populates local storage and fires when you click a button with the class .renderData
renderData: function() {
var self = this;
this.teams.each(function(team) {
var teamPlayers = this.players.where({team_id: team.get('id')})
_.each(teamPlayers, function(player) {
playersCollection.create({
name: player.get('name'),
team: player.get('team'),
team_id: player.get('team_id'),
number: player.get('number'),
points: player.get('points')
})
}, this);
}, this);
playersCollection.fetch();
return this;
}
This is obviously not the best way to go about it, but it worked and I was in such a hurry. The caveats are you have to click a button that populates the data, the collection is hard coded in, it's just overall not very elegant (but it works) the app did what it needed.
So big thanks to #VLS, I appreciate the effort to explain your code, and create a fiddle. Sorry I was so late!
You can extend your collection's fetch method and use it in conjunction with Backbone.localStorage, so inside your collection you'd have something like:
localStorage: new Backbone.LocalStorage("TestCollection"),
fetch: function(options) {
// check if localStorage for this collection exists
if(!localStorage.getItem("TestCollection")) {
var self = this;
// fetch from server once
$.ajax({
url: 'collection.json'
}).done(function(response) {
$.each(response.items, function(i, item) {
self.create(item); // saves model to local storage
});
});
} else {
// call original fetch method
return Backbone.Collection.prototype.fetch.call(this, options);
}
}
Demo: http://jsfiddle.net/5nz8p/
More on Backbone.localStorage: https://github.com/jeromegn/Backbone.localStorage

Circular Structure error .postJSON data

var selectFormula = $(htmlContainer).find("ins").map(function (i, el) {
return {
fName: $(el).attr("data-record-name"),
fID: $(el).attr("data-record-id"),
fContent: $(el).text()
}
//fContent: $(htmlContainer).each(function () { if (!$(this).text().trim().length) { $(this).remove(); } }),
});
//keep
//var selFormInner = $(htmlContainer).find("ins").map(function (i, el) { return {
// fName: $(htmlContainer).find("ins[data-record-name]"),
// fID: $(htmlContainer).find("ins[data-record-id]"),
// fContent: $(htmlContainer).find("ins").each(function () { if (!$(this).text().trim().length) { $(this).remove(); } })
//}
//}); //inner content (array)
if (selectFormula /*&& selFormInner.length*/) {
// Get formula HTML from server
$.postJSON(formulaUrl, {
//name: selFormName.map(function () {
// return $(this).data('record-name');
//}).toArray(),
////return information on the corresponding record id
//recordID: selFormID.map(function () {
// return $(this).data('record-id');
//}).toArray(),
//return infmoration on the corresponding content of ins.
//formula: selFormInner.map(function () {
// return $(this);
//}).toArray()
formula: selectFormula };
This is a part of my script file(all javascript) that is requesting to execute a server-side method with the shorthand $.postJSON. I keep running into this "Converting circular structure to JSON" It happens on this line: 'data': JSON.stringify(data) in the included postJSON script file.
My question is specifically focused on the on the circular structure. This could be wrong, but I think it highly likely that it is referring to my variable selectFormula declared at the top. What is circular about this structure? I have done some reading with people getting the same error but their examples seemed more obvious than mine, an object referring to itself etc.
This JSON data that i am passing to the server has a struct created in a similar manner in c# but that doesn't really matter since it doesn't hit my server side method, this error is all client side. As you can see with lots of my commented out code, I have tried quite a few things. All of them wrong of course!
Thanks in advance for any insights.
In my case, converting the structure to an array here stopped the Circular Structure error. Jquery's: .toArray() method. Then All I had to do was edit my server side method argument to match. Thanks anyway if anyone tried to work on this!

Variable scope. Use a closure?

I'm trying to grab all the URLs of my Facebook photos.
I first load the "albums" array with the album id's.
Then I loop through the albums and load the "pictures" array with the photos URLs.
(I see this in Chrome's JS debugger).
But when the code gets to the last statement ("return pictures"), "pictures" is empty.
How should I fix this?
I sense that I should use a closure, but not entirely sure how to best do that.
Thanks.
function getMyPhotos() {
FB.api('/me/albums', function(response) {
var data = response.data;
var albums = [];
var link;
var pictures = [];
// get selected albums id's
$.each(data, function(key, value) {
if ((value.name == 'Wall Photos')) {
albums.push(value.id);
}
});
console.log('albums');
console.log(albums);
// get the photos from those albums
$.each(albums, function(key, value) {
FB.api('/' + value + '/photos', function(resp) {
$.each(resp.data, function(k, val) {
link = val.images[3].source;
pictures.push(link);
});
});
});
console.log('pictures');
console.log(pictures);
return pictures;
});
}
You're thinking about your problem procedurally. However, this logic fails anytime you work with asynchronous requests. I expect what you originally tried to do looked something like this:
var pictures = getMyPhotos();
for (var i = 0; i < pictures.length; i++) {
// do something with each picture
}
But, that doesn't work since the value of 'pictures' is actually undefined (which is the default return type of any function without an actual return defined -- which is what your getMyPhotos does)
Instead, you want to do something like this:
function getMyPhotos(callback) {
FB.api('/me/albums', function (response) {
// process respose data to get a list of pictures, as you have already
// shown in your example
// instead of 'returning' pictures,
// we just call the method that should handle the result
callback(pictures);
});
}
// This is the function that actually does the work with your pictures
function oncePhotosReceived(pictures){
for (var i = 0; i < pictures.length; i++) {
// do something with each picture
}
};
// Request the picture data, and give it oncePhotosReceived as a callback.
// This basically lets you say 'hey, once I get my data back, call this function'
getMyPhotos(oncePhotosReceived);
I highly recommend you scrounge around SO for more questions/answers about AJAX callbacks and asynchronous JavaScript programming.
EDIT:
If you want to keep the result of the FB api call handy for other code to use, you can set the return value onto a 'global' variable in the window:
function getMyPhotos(callback) {
FB.api('/me/albums', function (response) {
// process respose data to get a list of pictures, as you have already
// shown in your example
// instead of 'returning' pictures,
// we just call the method that should handle the result
window.pictures = pictures;
});
}
You can now use the global variable 'pictures' (or, explicitly using window.pictures) anywhere you want. The catch, of course, being that you have to call getMyPhotos first, and wait for the response to complete before they are available. No need for localStorage.
As mentioned in the comments, asynchronous code is like Hotel California - you can check any time you like but you can never leave.
Have you noticed how the FB.api does not return a value
//This is NOT how it works:
var result = FB.api('me/albums')
but instead receives a continuation function and passes its results on to it?
FB.api('me/albums', function(result){
Turns out you need to have a similar arrangement for your getMyPhotos function:
function getMyPhotos(onPhotos){
//fetches the photos and calls onPhotos with the
// result when done
FB.api('my/pictures', function(response){
var pictures = //yada yada
onPhotos(pictures);
});
}
Of course, the continuation-passing style is contagious so you now need to call
getMyPhotos(function(pictures){
instead of
var pictures = getMyPhotos();

Categories