JS <select> listbox not working on click/change - javascript

what I need to do is to gather some data online via fetch and use the data to give the user infos and such (that I can do). In some cases, the fetch can return multiple results (an array of data), so I decided to create a element in the page to list all the names of the elements in the array (each element in the array is a sub-array with various infos, also a "name", which would be a location name).
I'm working on Chrome. All works, I store the data and this way I created another function to (theoretically) give the user the infos about the selected option in the listbox. If I call the function directly it works fine, meaning it returns all the infos it should. What actually doesn't work is the fact that this function returning infos should trigger on click/selecting any option from the list and it doesn't work. I don't get any error messages, it just does nothing.
What you see in this image is what happens when you click "filter" (it actually filters world locations detected with name similar to "Milano"), the user gets infos in the page and what you see in the right log side in the red square is returning the content of that array I mentioned (it contains data, so it's working), the index selected in the list (the last, so it's 5) and the coordinates of such selected location.
This happens just after the creation of the options because I directly call the function (returning data about the selected option), but if I use manually the list it just does nothing (as you see in the list, there's nothing logged after those coordinates).
I tried to create the options like this:
function createOption(text){
let listOption = new Option(text, text, true, true);
return listOption;
}
What I do directly to create the options in the list is a "for" loop for each element i in the array:
document.getElementById("keyword-results").append(createOption(data.data[i].station.name));
I also tried appendChild instead of append. As I said, this works, but the manual use of the list doesn't. What I declared is:
document.getElementById("keyword-results").addEventListener("onchange", selection());
being "selection()" the function returning the data.
function selection(){
let index = document.getElementById("keyword-results").selectedIndex;
console.log(index);
if (index > -1){
let currentResult = results.data[index];
let aqi = currentResult.aqi;
console.log(aqi);
document.getElementById("answer").innerHTML += `The estimated AQI [...]`;
let far = distance(currentResult.station.geo[0], currentResult.station.geo[1]);
if (far != null || far != undefined) {
document.getElementById("answer").innerHTML += `The estimated distance [...]`;
}
}
}
I also tried "change", "onclick" and "click" as methods to trigger the function but none of them works. Do you have any suggestions? I can't find anything useful here on stackoverflow nor on the web. If you want to check the whole code, this is my GitHub repository https://github.com/leorob88/pollution-forecast-API

document.getElementById("keyword-results").addEventListener("onchange", selection());
addEventListener expects a function as a second parameter, selection() is not a function but selection is.
When you use addEventListener, there is no such event named onchange its actually change
so it becomes:
document.getElementById("keyword-results").addEventListener("change", selection);
Reference.

Related

How to access an entered Interactive Grid column value in a Javascript dynamic action on the Change event in order to ensure uniqueness

I am trying to prevent duplicate items from being entered in an Interactive Grid in Oracle Apex 20.2. I do get a unique constraint error when this happens, but this is for a barcode scanning stock control app and the unique constraint error only happens when saving after scanning a room with lots of objects. It is then very difficult to find the duplicate field. You also cannot use sort, since that wants to refresh the page and looses all your scanned items. I cannot presort because I want the last scanned item on top.
I was able to add Javascript on page load that creates an array with all the barcodes. I then check this array when scanning and do not add new Interactive Grid rows when a duplicate barcode is going to be added to the array.
In addition to this I need to add the same for when an Interactive Grid row is manually entered. For this I wanted to add a Javascript dynamic action on the barcode column in the Interactive Grid, in order to once again check the global array for uniqueness. However I have several issues: I cannot figure out how the get the entered barcode value in the change dynamic action Javascript, sometimes it shows the previous changed value (might be this bug although I am in 20.2) and the Change event also seems to fire twice when hitting enter after entering a value (once for the new row (this time my code works unlike when hitting Tab) and once for the next row below). The last one seems bad, since then it will try to check existing values (the next row) and give errors that should not happen; however I do not see a more appropriate event like On Row Submit. Not sure if there is a way to check whether the value changed on the Change event.
The code I currently have I got from here. I am assuming this means Oracle Apex does not have a standard way of getting an Interactive Grid column value in a Javascript dynamic action. Not sure if this has changed in 20.2 or 21. The code I have is:
console.log($(this.triggeringElement));
var grid = apex.region('LINES').widget().interactiveGrid('getViews', 'grid');
var model = grid.model;
var selectedRow = grid.view$.grid('getSelection');
var id = $(selectedRow[0][0]).data('id');
var record = model.getRecord(id);
let bcode = model.getValue(record, 'BARCODE');
console.log(id);
console.log(record);
console.log($(selectedRow[0][0]));
console.log(bcode);
if(barcodes.includes(bcode)) {
apex.message.showErrors([{
type: "error",
location: "page",
message: "The entered barcode is already in the list.",
unsafe: false
}]);
}
When I console.log(record) I can see values that I enter into the barcode column, but I do not know how to walk the object tree in order to retrieve the value out of the record. I do not understand the object it shows me in the console log. It does not seem to correlate with the dot access traversals that others are doing in the above code. I can see the record array at the top, but for that the barcode column shows the previous value; below that it does however show the newly entered value as the third (2) index, but I do not know how to write Javascript to access that.
If I can access the previous and new value from the record object I could check for changes and also compare the new value against the global array. How do I access these values in the record object or is there a better way of achieving my goal? bcode prints the previous value, so I guess I already have that if that is not a bug.

Return Array of Data using Google Assistant from Firebase

The structure I have for my firebase database is like this:
fruits:
apple,5
banana,6
I want to put apple and banana in an array so that when i give a command to Google Assistant, it would give me apple, 5 and banana, 6. The code I have is like the one below:
function handleCommand(agent) {
return admin.database().ref('Fruits').child().once("value").then((snapshot) =>{
var i;
var fruitlist=[];
//puts each snapshot child of 'Fruit' in an array
snapshot.forEach(function(item) {
var itemVal = item.val();
fruitlist.push(itemVal);
});
//outputs command in google assistant
for (i=0; i < fruitlist.length; i++) {
agent.add(fruitlist[i]);
}
})
The default response is "not available".
I get the following in the execution logs:
Firebase.child failed. Was called 0 aruguments. expects at least 1.
I do not know which argument to put inside the Firebase.child. if i want all fruits to be "spoken" by Google Assistant. Below is a picture of my firebase structure.
The error looks like the one below:
What I am currently doing now to just output the fruits are manually entering each child in the code like this and removed the ".child" in the return statement:
Which gives me the output below which is also what I want to see but using arrays as the solution I am using now is very much hardcoded:
As the error message suggests, and as you surmise, the child() call expects a parameter - in particular, the name of the child node you want to get information from. However, since you want all the children of the "Fruits" node - you don't need to specify it at all. The child() call just navigates down through the hierarchy, but you don't need to navigate at all if you don't want to.
The snapshot you get back will have a value of the entire object. In some cases, this can be pretty large, so it isn't a good idea to get it all at once. In your case, it is fairly small, so not as big a deal.
On the JavaScript side, you can now handle that value as an object with attributes and values. Your original code didn't quite do what you said you want it to, however - you're getting the value, but ignoring the name (which is the attribute name or key). You can iterate over the attributes of an object in a number of ways, but I like getting the keys of the object, looping over this, getting the value associated with the key, and then "doing something" with it.
While I haven't tested the code, it might look something like this:
function handleCommand(agent) {
return admin.database().ref('Fruits').once("value").then((snapshot) =>{
// Get an object with all the fruits and values
var fruits = snapshot.val();
// Get the keys for the attributes of this object as an array
var keys = Object.keys( fruits );
// Iterate over the keys, get the associated value, and do something with it
for( var i=0; i<keys.length; i++ ){
var key = keys[i];
var val = fruits[key];
agent.add( `The number of ${key} you have are: ${val}` );
}
})
While this is (or should be) working Firebase and JavaScript, there are a couple of problems with this on the Actions on Google side.
First, the message returned might have some grammar problems, so using your example, you may see a message such as "The number of Apple you have are: 1". There are ways to resolve this, but keep in mind my sample code is just a starter sample.
More significantly, however, the call to agent.add() with a string creates a "SimpleResponse". You're only allowed two simple responses per reply in an Action. So while this will work for your example, it will have problems if you have more fruit. You can solve this by concatenating the strings together so you're only calling agent.add() once.
Finally, you may wish to actually look at some of the other response options for different surfaces. So while you might read out this list on a speaker, you may read a shorter list on a device with a screen and show a table with the information. Details about these might be better addressed as a new StackOverflow question, however.

AngularJS - NgOptions from function returning undefined values

I've got an application which has to show some options inside a select depending on a previous option. To achieve this, I've put the logic to show those options inside a controller function, like this:
vm.getScopeValues = function(tp){
var vals = [];
vals.push({n: 'Opt1', v: 'opt1'});
if(!tp || tp != 'somevalue'){
vals.push({n: 'Opt2', v: 'opt2'});
}
return vals;
};
The problem is that sometimes, I get an undefined as the value of the select options. On the following jsFiddle you can see a simpler approach, but with same result:
https://jsfiddle.net/nr9ffrkk/
As you can see:
If I write track by v, I get undefined
If I write track by o.v, then the values are right, but the ng-model does not get matched correctly
If I write o.v as o.n... without the track by, I get an error ($digest cycles)
When everything gets matched correctly (I achieved it somehow but cannot remember how), if I add a new dynamic "field" (click on "ADD NEW FIELD"), then they come out as undefined values again
I've made console.log to watch the returning values of the function, and it gets called correctly and returns the right values (nothing undefined).
I need to be able to create dinamycally new entries on the fields array (with an initial value for type, of course) and that the select gets the right values for the options.
UPDATED
As you can see, making a controller variable (not a function) with the values does not work neither, bringing the same problem:
https://jsfiddle.net/dgy5ryvh/
Thank you!

Javascript - removing an element from array

I'm displaying elements from an arraylist in table on the webpage. I want to make sure that once the user press "delete the data", the element in the table is immediately removed so the user does not have to refresh and wait to see the new table. So I'm currently doing it by removing the element from the arraylist, below is the code:
$scope.list= function(Id) {
var position = $scope.list.indexOf(fooCollection.findElementById({Id:Id}));
fooCollection.delete({Id:Id});
if (position>-1) {
$scope.list.splice(position,1);
}
$location.path('/list');
};
But I the position is always -1, so the last item is always removed from the list no matter which element I delete.
I found it strange you we're operating on two different lists to begin with so I assumed you were taking a copy of the initial list. This enabled me to reproduce your bug. On the following line you're trying to find an object that isn't present in your list.
var position = $scope.list.indexOf(fooCollection.findElementById({Id:Id}));
Eventhough we're talking about the same content, these two objects are not the same because:
indexOf compares searchElement to elements of the Array using strict
equality (the same method used by the ===, or triple-equals,
operator).
So there lies your problem. You can see this reproduced on this plunker.
Fixing it the quick way would mean looping over your $scope.list and finding out which element actually has the id that is being passed.
you can use the splice method of javascript which takes two paramete
arrayObject.splice(param1, param2);
param1 -> from this index elements will start removing
param2 -> no of elements will be remove
like if you want to remove only first element and your array object is arrayObject then we can write code as following
arrayObject.splice(0, 1);

ObservableArray child observable change monitoring

Consider this jsfiddle.
I can't think of a way to ensure that if row one in the above example has already been selected in the dropdown that the next row would be prevented from selecting the same value.
I think that my problem here is that when the dropdown click event fires, the subscriber does not monitor this change when the child value has changed. Anyone able to assist?
viewModel.actualMetrics.subscribe(function(newValue) {
if (newValue) {
$.each(viewModel.actualMetrics(), function(n, item) {
if (item.MetricTypeId() == newValue.MetricTypeId)
alert("already selected this Metric");
});
}
Here is a basic sample of one way to do what you want: http://jsfiddle.net/rniemeyer/3cpUp/
Here is your sample with it: http://jsfiddle.net/rniemeyer/8bQmq/
The basic idea is that you have your list of choices, then you create a dependentObservable that is an index of the currently used choices. This saves some looping through the current choices when building each rows options. This index could be an object or an array. I used an object, but you could use an array as well with the id as the index.
Then, on each item, you could have a dependentObservable to store the filtered choices for that item. However, I used a function instead, because it does not seem like a property that is really important to the view model and bindings are implemented using dependentObservables, so you get the same effect without having the choices show up when you send it toJSON. The function loops through all of the choices and includes only the choices that do not appear on another line by checking its own value and the index.

Categories