Promise in MagicSuggest data function - javascript

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[]>

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 -.

Populate an array with meta data gathered from Xray

I've been trying to fill an array with metadata that I collect with Xray, and haven't had any success. The function is called by an API route on my server and gets the links from my application.
I seem to be struggling with promises as it takes time to scrape the metadata, and I can't seem to get the function to wait until the data has been collected before moving on. Perhaps, I'm just not understanding how Xray works? Or maybe promises? I've tried everything I can think of, this being the most recent attempt (and the simplest):
function createCollection() {
Promise.all(rawLinks.map(function(link) {
linksArray.push(xray(link, 'title')(function(error, title) {
console.log(title);
return title;
}))
}))
.then(linksArray => {
console.log(linksArray);
});
}
It's by far not the most robust or elaborate solution I've tried, but it's the most recent one. First the console logs an array with "undefined" as the data, THEN it logs the individual titles.
I would be very thankful for any help, or direction on what to research. Like I've said, I feel as if I've exhausted all my ideas and don't know where to even look anymore.
Figured it out, this seems to be doing the trick!
// format links into an array of objects
var rawLinks = links.split(', ');
var linksArray = [];
createCollection();
function createCollection() {
rawLinks.map(function(link) {
var fillMetaPromise = new Promise(
function(resolve, reject) {
var test = xray(link, 'title')(function(err, title) {
var data = { title: title, link: link };
resolve(data);
});
})
.then(data => {
processTitle(data.title, data.link);
});
});
}
function processTitle(title, link) {
var object = {
link: link,
title: title
};
linksArray.push(object);
console.log(linksArray);
}

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!

using json data from localhost in js file using ruby

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.

Dojo fetch, how to wait on two simultaneous async fetches?

Hey guys, im not well versed in dealing with asynchronous design patterns, and im having a problem writing a script that does two async data fetches.
Im using Dojo.data.api.Read.Fetch() to make two fetch() calls from seperate databases. The reulsts come back asynchronously. However, I have to cross reference the results, so i want my script to continue once BOTH async fetches are complete. I dont know how to do this, and therein lies the problem.
I am aware of the fetch's onComplete field and how to use it, BUT the best case solution i see there is to call the second fetch in the onComplete of the first fetch. I would like to do these fetches at the same time. Is there a way to do this?
Here's the current structure of my program for illustration purposes:
this.dict1.fetch({query:"blahblahblah", onComplete: function(items) { something here? }});
this.dict2.fetch({query:"blahblahbleh", onComplete: function(items) { or maybe something here? }});
this.orMaybeDoSomethingAfterBothFetches()
Any help would be greatly appreciated!
You could create dojo.Deferreds for each of the fetches and then use dojo.DeferredList and add the deferreds to it - see here. This solution allows you to take advantage of adding 'n' functions to the list of functions you want to call. It also takes advantage of all the dojo.Deferred's callback and errBack functionality.
var fetch1 = new dojo.Deferred();
fetch1.addCallback(this.dict1.fetch...);
var fetch2 = new dojo.Deferred();
fetch2.addCallback(this.dict2.fetch...);
var allFuncs = new dojo.DeferredList([fetch1, fetch2]);
var doStuffWhenAllFuncsReturn = function() {...};
allFuncs.addCallback(doStuffWhenAllFuncsReturn);
// this is a variation of a function I have answered quite a few similar questions on SO with
function collected(count, fn){
var loaded = 0;
var collectedItems = [];
return function(items){
collectedItems = collectedItems.concat(items);
if (++loaded === count){
fn(collectedItems);
}
}
}
var collectedFn = collected(2, function(items){
//do stuff
});
this.dict1.fetch({query:"blahblahblah", onComplete: collectedFn);
this.dict2.fetch({query:"blahblahbleh", onComplete: collectedFn);
An alternative solution is
var store = {
exec: function(){
if (this.items1 && this.items2) {
// do stuff with this.items1 and this.items2
}
}
};
this.dict1.fetch({query:"blahblahblah", onComplete: function(items) {
store.items1 = items;
store.exec();
});
this.dict2.fetch({query:"blahblahbleh", onComplete: function(items) {
store.items2 = items;
store.exec();
});

Categories