How do I get Data from Algolia searchClient to function in JavaScript? - javascript

I have a problem to get data from variables inside an inner Javascript function to the outer function.
What I am trying to do is to search Algolia-hits from a special category to show the first hit that has an image. So I can use this image for an category-overview. All works fine, except that i dont manage to get the img-Url from the hits.
My function looks like:
methods: {
getCategoryPicture(categoryName) {
const index = this.searchClient.initIndex("Testindex");
var FacetFilterString = "Kategorie: " + categoryName;
var imgUrl = "";
// search for hits of category
index
.search("", {
facetFilters: [FacetFilterString],
attributesToRetrieve: ["img", "Marke"],
hitsPerPage: 50
})
.then(({ hits }) => {
// search for first hit with img
var img = ""
hits.forEach((element) => {
if (element["img"] != undefined)
{
img = "www.someurl.com/" + element["img"];
console.log(img);
return img
}
});
this.imgUrl = img
return img;
});
return imgUrl;
},
},
The function finds the url, I can log it, but I dont manage to get it from the inner ".then"-function to the outer function. I cant reach the variables of the outer-function. "this" doesnt work eather. (because it points to the module, not to the outer function, i guess?).
I just want to return the first img-url the function finds.
Edit: I updated the Code, so it might be more clear. The function "setImgUrl" logs the picture perfectly, but it does not update the outer variable "imgUrl".
methods: {
getCategoryPicture(categoryName) {
const index = this.searchClient.initIndex("Testindex");
var FacetFilterString = "Kategorie: " + categoryName;
var imgUrl = "";
index
.search("", {
facetFilters: [FacetFilterString],
attributesToRetrieve: ["img", "Marke"],
hitsPerPage: 50
})
.then(({ hits }) => {
var img = ""
hits.forEach((element) => {
if (element["img"] != undefined)
{
img = "www.someurl.com/" + element["img"];
setImgUrl(img);
}
});
return img
});
function setImgUrl(img) {
imgUrl = img;
console.log(img);
}
return imgUrl;
},
},

Related

JS - How to retrieve variable after IndexedDB transaction.oncomplete() executes?

My problem is simple, but incredibly frustrating as I'm now on my second week of trying to figure this out and on the verge of giving up. I would like to retrieve my 'notesObject' variable outside my getAllNotes() function when after the transaction.oncomplete() listener executes.
(function() {
// check for IndexedDB support
if (!window.indexedDB) {
console.log(`Your browser doesn't support IndexedDB`);
return;
}
// open the CRM database with the version 1
let request = indexedDB.open('Notes', 1);
// create the Contacts object store and indexes
request.onupgradeneeded = (event) => {
let db = event.target.result;
// create the Notes object store ('table')
let store = db.createObjectStore('Notes', {
autoIncrement: true
});
// create an index on the sections property.
let index = store.createIndex('Sections', 'sections', {
unique: true
});
}
function insertData() {
let myDB = indexedDB.open('Notes');
myDB.onsuccess = (event) => {
// myDB.transaction('Notes', 'readwrite')
event.target.result.transaction('Notes', 'readwrite')
.objectStore('Notes')
.put({
sections: "New Note",
pages: "New page",
lastSelectedPage: ""
});
console.log("insert successful");
}
myDB.onerror = (event) => {
console.log('Error in NotesDB - insertData(): ' + event.target.errorCode);
}
myDB.oncomplete = (event) => {
myDB.close();
console.log('closed');
}
}
insertData()
function getAllNotes() {
let myDB = indexedDB.open('Notes');
let notesObject = [];
myDB.onsuccess = (event) => {
let dbObjectStore = event.target.result
.transaction("Notes", "readwrite").objectStore("Notes");
dbObjectStore.openCursor().onsuccess = (e) => {
let cursor = e.target.result;
if (cursor) {
let primaryKey = cursor.key;
let section = cursor.value.sections;
notesObject.push({
primaryKey,
section
})
cursor.continue();
}
}
dbObjectStore.transaction.onerror = (event) => {
console.log('Error in NotesDB - getAllData() tranaction: ' + event.target.errorCode);
}
dbObjectStore.transaction.oncomplete = (event) => {
return notesObject;
console.log(notesObject)
}
}
}
let notes = getAllNotes()
console.log("Getting Notes sucessful: " + notes)
})()
I've tried setting global variables, but nothing seems to work. I am a complete noob and honestly, I'm completely lost on how to retrieve the notesObject variable outside my getAllNotes() function. The results I get are 'undefined'. Any help would be greatly appreciated.
This is effectively a duplicate of Indexeddb: return value after openrequest.onsuccess
The operations getAllNotes() kicks off are asynchronous (they will run in the background and take time to complete), whereas your final console.log() call is run synchronously, immediately after getAllNotes(). The operations haven't completed at the time that is run, so there's nothing to log.
If you search SO for "indexeddb asynchronous" you'll find plenty of questions and answers about this topic.

load different module on preferences

I would like to process an array of image files. When selecting them I can choose between selecting them randomly or one by one (queue). The decision is hold by the config.json.
First I initialize the processor and select the right image selection module by calling processImages and pass in the array of image files.
function processImages(images) {
const imageSelector = getImageSelector();
imageSelector.init(images); // initialize the module
console.log(imageSelector.getImage()); // test
}
function getImageSelector() {
const { random } = config;
const selector = random ? 'randomSelector' : 'queueSelector';
return require(`./imageSelectors/${selector}.js`);
}
The modules itself have their own logic to return the next image. For the random module I go for
let images;
module.exports = {
init: images => {
init(images);
},
getImage: () => {
return getImage();
}
};
function init(images) {
this.images = images;
}
function getImage() {
const index = getRandomIndex();
return images[index];
}
function getRandomIndex() {
const indexValue = Math.random() * images.length;
const roundedIndex = Math.floor(indexValue);
return images[roundedIndex];
}
and for the queue module I go for
let images;
let currentImageCounter = 0;
module.exports = {
init: images => {
init(images);
},
getImage: () => {
return getImage();
}
};
function init(images) {
this.images = images;
currentImageCounter = 0;
}
function getImage() {
const index = getNextIndex();
return images[index];
}
function getNextIndex() {
if (currentImageCounter > images.length)
currentImageCounter = 0;
else
currentImageCounter++;
return currentImageCounter;
}
When I run the code and random is true I get this error
C:...\imageSelectors\randomSelector.js:22
const indexValue = Math.random() * images.length;
TypeError: Cannot read property 'length' of undefined
When calling imageSelector.init(images) some image items are available so they just are undefined within the modules.
I think I missunderstood how to work with modules correctly. Could someone tell me how to setup the code correctly?
In your module, you declare a local variable images and use it as an object field this.images which is not right. Try this approach and do not shadow the upper-level variables with deeper-level variables with the same names to not be misled (i.e. use images as outer variable and something like imgs as function parameters). I've also a bit simplified your module code to avoid some unneeded duplication.
let images;
module.exports = {
init(imgs) {
images = imgs;
},
getImage() {
const index = getRandomIndex();
return images[index];
}
};
function getRandomIndex() {
const indexValue = Math.random() * images.length;
const roundedIndex = Math.floor(indexValue);
return images[roundedIndex];
}

Matching text in element with Protractor

I have an element on page. And there could be different text. I am trying to do like (code is below), and it is not printed to console.
this.checkStatus = function () {
var element = $('.message')
browser.wait(EC.visibilityOf(element), 5000).then(function () {
browser.wait(EC.textToBePresentInElement(conStatus, 'TEXT1'), 500).then(function () {
console.log('TEXT1');
})
browser.wait(EC.textToBePresentInElement(element, 'TEXT2'), 500).then(function () {
console.log('TEXT2');
})
browser.wait(EC.textToBePresentInElement(element, 'TEXT3'), 500).then(function () {
console.log('TEXT3');
})
browser.wait(EC.textToBePresentInElement(element, 'TEXT4'), 500).then(function () {
console.log('TEXT4');
})
})
return this;
}
thanks
I see two problems. first, not sure what 'constatus' is? you need to correct that. second, browser.wait will be throwing error/exceptions when it is not able to find matching condition and timeout expires, So, if your first condition doesn't meet, it will throw timeout exception and will never go to second one. Instead, try something like below
var section = "";
this.checkStatus = function () {
var element = $('.message')
browser.wait(EC.visibilityOf(element), 5000).then(function () {
browser.wait(()=>{
if(EC.textToBePresentInElement(element, 'TEXT1')){
section = "Text1";
}
else if(EC.textToBePresentInElement(element, 'TEXT2')) {
section = "Text2";
}
else if(EC.textToBePresentInElement(element, 'TEXT3')) {
section = "Text3";
}
else if(EC.textToBePresentInElement(element, 'TEXT4')) {
section = "Text4";
}
if(section !== "")
return true;
}, 5000).then(()=>{
<here you can do anything based on 'section'>
}
Note - I haven't verified compilation errors.. so check for that.
Not sure what are you up to, but you can join multiple expected conditions with "or":
var conStatus = $('.message');
var containsText1 = EC.textToBePresentInElement(conStatus, 'TEXT1');
var containsText2 = EC.textToBePresentInElement(conStatus, 'TEXT2');
var containsText3 = EC.textToBePresentInElement(conStatus, 'TEXT3');
var containsText4 = EC.textToBePresentInElement(conStatus, 'TEXT4');
browser.wait(EC.or(containsText1, containsText2, containsText3, containsText4), 5000);

Protractor - Error: Index out of bound exception while using the same function for the second time

I have the following function which selects a category from a list of available categories. This function works fine in my first test. But the same function with a different valid category name in my second test fails with the following error.
Error: Index out of bound. Trying to access element at index: 0, but there are only 0 elements that match locator By.cssSelector(".grid-view-builder__category")
this.categoryElements = element.all(by.css('.grid-view-builder__category'));
this.selectCategory = function (categoryName) {
var filteredCategories = this.categoryElements.filter(function (category) {
return category.getText().then(function (text) {
log.info(text);
return text === categoryName;
})
})
filteredCategories.first().click().then(function () {
log.info("Select Category: " + categoryName);
}).then(null, function (err) {
log.error("Category: " + categoryName + " Not Found !!" + err);
});
}
Spec File
var columnSelect = require('pages/grid/columns/columnselector-page')()
it('Add Publisher ID Column to the Grid & Verify', function () {
var columnCountBefore = columnSelect.getColumnCount();
columnSelect.openColumnSelector();
columnSelect.selectCategory('Advanced');
columnSelect.selectColumn('Publisher ID');
columnSelect.apply();
var columnCountAfter = columnSelect.getColumnCount();
expect(columnCountAfter).toBeGreaterThan(columnCountBefore);
});
The problem might be in the way you are defining and using Page Objects. Here is a quick solution to try - if this would help, we'll discuss on why that is happening.
Make the categoryElements a function instead of being a property:
this.getCategoryElements = function () {
return element.all(by.css('.grid-view-builder__category'));
};
this.selectCategory = function (categoryName) {
var filteredCategories = this.getCategoryElements().filter(function (category) {
return category.getText().then(function (text) {
log.info(text);
return text === categoryName;
})
})
filteredCategories.first().click().then(function () {
log.info("Select Category: " + categoryName);
}).then(null, function (err) {
log.error("Category: " + categoryName + " Not Found !!" + err);
});
}
Or, this could be a "timing issue" - let's add an Explicit Wait via browser.wait() to wait for at least a single category to be present:
var EC = protractor.ExpectedConditions;
var category = element(by.css('.grid-view-builder__category'));
browser.wait(EC.presenceOf(category), 5000);
It looks like this has nothing to do with the code posted here, only that the css selector you're using is not finding any elements

API Key with colon, parsing it gives TypeError: forEach isn't a function

I'm trying to use New York Times API in order to get the Top Stories in JSON but I keep on getting a:
Uncaught TypeError: top.forEach is not a function
I feel like there's something wrong with the API key since it has : colons in the url. I even tried to encode it with %3A but it still doesn't work.
This is the basic url:
http://api.nytimes.com/svc/topstories/v2/home.json?api-key={API-KEY}
My function that grabs the data from the url:
```
function topStories(topStoriesURL) {
$.getJSON(topStoriesURL, function(top) {
top.forEach(function(data) {
link = data.results.url;
cardTitle = data.results.title;
if(data.results.byline == "") { postedBy = data.results.source; }
else { postedBy = data.results.byline; }
imgSource = data.results.media[0].media-metadata[10].url;
createCardElements();
});
});
}
I console.log(url) and when I click it inside Chrome console, it ignored the part of the key that comes after the colon. I've been debugging, but I can't seem to figure out the error.
Here is a version of the code that works.
function topStories(topStoriesURL) {
$.getJSON(topStoriesURL, function(data) {
if (data.error) {
alert('error!'); // TODO: Add better error handling here
} else {
data.results.forEach(function(result) {
var link = result.url,
cardTitle = result.title,
postedBy = result.byline == "" ? result.source : result.byline,
hasMultimedia = (result.multimedia || []).length > 0,
imgSource = hasMultimedia ? result.multimedia[result.multimedia.length - 1].url : null;
createCardElement(link, cardTitle, postedBy, imgSource);
});
}
});
}
function createCardElement(link, title, postedBy, imgSource) {
// create a single card element here
console.log('Creating a card with arguments of ', arguments);
}
topStories('http://api.nytimes.com/svc/topstories/v2/home.json?api-key=sample-key');
You are most likely going to need to do a for ... in loop on the top object since it is an object. You can not do a forEach upon on object the syntax would probably look like this:
function topStories(topStoriesURL) {
$.getJSON(topStoriesURL, function(top) {
for (var datum in top) {
link = datum.results.url;
cardTitle = datum.results.title;
if(datum.results.byline == "") { postedBy = datum.results.source; }
else { postedBy = datum.results.byline; }
imgSource = datum.results.media[0].media-metadata[10].url;
createCardElements();
});
});
}
Heres the documentation on for...in loops

Categories

Resources