Dynamically setting options for a select elements skips first element - javascript

I've dynamically built an options array divided in two options group. These options groups are stored in a javascript array named ogs. The code for the same is as below:
var ogs = [];
for (optGroup in optionsList) {
var og = document.createElement('optgroup');
og.label = optGroup;
var ops = optionsList[optGroup];
for (op in ops) {
var o = document.createElement('option');
o.value = op;
o.text = ops[op];
og.appendChild(o);
}
ogs.push(og);
}
Now, I'm trying to add these options to 2 select elements, as below:
var from_el = document.getElementById('from_selector'), to_el = document.getElementById('to_selector');
for (i = 0; i < ogs.length; ++i) {
var og = ogs[i];
from_el.add(og);
to_el.add(og);
}
However, at the end of the script, only to_selector has the options populated, whereas from_selector remains empty. The reason I'm populating the options like this is because both these select elements use select2, and any other method (such as innerHTML) is taking significantly longer. I've also tried putting these selectors in array and iterating over them, always the last select gets populated, whereas first select remains empty.

Figured out the issue. The problem was in the following lines:
from_el.add(og);
to_el.add(og);
When og was getting added to to_el, it was getting detached from from_el. The solution was to replace this with.
from_el.add(og);
to_el.add(og.cloneNode(true));

Related

How to automatically add questions in a Google Form based on columns on Google Sheets? [duplicate]

I'm new with Google scripts and now I have to make a form with a list of choices. These choices should be picked up from the Google sheet.
So the first question is how to chose only unique values from some range of my spreadsheet?
The second is how to pass this list so that they will be the items in the list?
The code I've tried is:
function getMembranesList() {
var ss = SpreadsheetApp.openByUrl("https://docs.google.com/spreadsheets/......");
var itemList = ss.getSheetByName('Answers').getRange("Q1:Q").getValues();
var form = FormApp.getActiveForm();
var item = form.addListItem()
item.setTitle('test question');
item.createChoice(itemList);
}
Looking at the methods available to populate the ListItem, you have to choose one and set your data up so it matches the expected input. For my example, I chose the setChoiceValues method, which looks for an array. So I have to manipulate the items into an array.
One thing the getRange.getValues() method does NOT get you is how many non-blank items are returned in the list. I used this quick way to get a count of those items, so I have a maximum bound for my loops. Then, I formed the itemArray and added only the non-blank items to it.
After that, it's just a matter of creating the ListItem and adding the values:
function getMembranesList() {
var ss = SpreadsheetApp.openByUrl("https://docs.google.com/spreadsheets/...");
var itemList = ss.getSheetByName('Answers').getRange("Q1:Q").getValues();
var itemCount = itemList.filter(String).length;
var itemArray = [];
for (var i = 0; i < itemCount; i++) {
itemArray[i] = itemList[i];
}
var form = FormApp.getActiveForm();
var item = form.addListItem();
item.setTitle('test question');
item.setChoiceValues(itemArray);
}

Populate select drop-down based on choice of another select form field

Here is my fiddle : DEMO
Under the "Rules" Tab, on click of "+" a group of form-fields are cloned i.e, Join operator, Attributes, Operator & Threshold.
The attribute drop down is populated using a json (called expressionDetails) created using the relationship between contracts and thresholds variables.
Based on the choice of attributes, the thresholds field will be populated.
I could achieve this for the non-cloned Attribute and Threshold. However, due to class/ id duplication I am not able to pick up the cloned attribute's value as all the clones attributes hold the same class and their values are getting concatenated (in var z1).
//Appending option to "cloned" thresold field based on choice of attribute
$('.attributeExpr').on('change', function(e) {
$('.thresholdExpr').empty();
var z1 = $(".attributeExpr option:selected").text();
console.log(z1);
var a1 = expressionDetails[z1];
console.log(a1);
for (var i1 = 0; i1 < a1.length; i1++) {
var b1 = a1[i1].name;
// alert(b1);
var opt1 = $("<option>").text(b1);
// console.log(opt1);
$('.thresholdExpr').append(opt1);
}
});
Is there a different approach for this? Also, it should work for every cloned group thereafter as I will be using all of these values to create the "Expression" field.
Any help would be much appreciated. Thank you.
Replace the line 3 in above code with this. it will only return selected value.
var z1 = $("option:selected",$(this)).text();
Have you tried something like:
var z1 = $(this).find('option:selected').text();
Instead
var z1 = $(".attributeExpr option:selected").text();
Try this, I have tested it in your fiddle DEMO and it is working.
$('.attributeExpr').on('change', function(e) {
var index = $(this).index('.attributeExpr');
$('.thresholdExpr:eq( '+index+' )').empty();
var z1 = $(this).find("option:selected").text();
console.log(z1);
var a1 = expressionDetails[z1];
console.log(a1);
for (var i1 = 0; i1 < a1.length; i1++) {
var b1 = a1[i1].name;
// alert(b1);
var opt1 = $("<option>").text(b1);
// console.log(opt1);
$('.thresholdExpr:eq( '+index+' )').append(opt1);
}
});
I have added index so it can target the current element.
First thing you should do to make it work properly specially such complex implementation is to make the expressionsBuilder formgroup option fields to be dynamic, means it is being populated by JavaScript and not hard-coded in your HTML.
Then, you will assign the change event listener for each individual fields you created, this way you can control every form-group's behavior.
example click here
by populating it programatically you have total control of the fields behavior. You can then get each and every value of fields by iterating expressions variable like this:
for (var i = 0; i < expressions.length; i++)
{
var id = expressions[i];
var theAttribute = $("#" + id).find("[name='attribute']").val();
var theOperator = $("#" + id).find("[name='operator']").val();
var theThreshold = $("#" + id).find("[name='threshold']").val();
}
Hope that helps
===
ALSO heads up, it appears you are creating such complex application. I am suggesting you should make use for JavaScript frameworks to ease up maintainability of your code. This approach will become very hard to maintain in the long run

How to get selected values of Kendo Multi Select?

I'm using Kendo multi select as follow but i can't get selected values
var multiselect = $("#SelectRoles").data("kendoMultiSelect");
var selectedData= [];
var items = multiselect.value();
for (var itm in items)
{
selectedData.push(itm);
}
but array selectedData return indices of items in multiselect not values .
You can also assign the array, returned from the value() method, directly to the variable, e.g.:
var ms = $("#multiselect").kendoMultiSelect({
value: ["1", "2"]
}).data('kendoMultiSelect');
var selectedItems = ms.value();
console.log(selectedItems); // ["1", "2"]
Use this other one returns indices.
var multiselect = $("#SelectRoles").data("kendoMultiSelect");
var selectedData= [];
var items = multiselect.value();
for (var i=0;i<items.length;i++)
{
selectedData.push(items[i]);
}
Your original code doesn't look wrong. Are you sure you are getting only indices? Perhaps you should post your MultiSelect code as well. I found this question because I had the same problem and used the other answers for reference, but I found them overcomplicated. So let me answer in another complicated way :)
Here's what I've got. I know it's more code than you need, but I think it's important to see the full picture here. First let me set this up. There's a problem with the Kendo().MultiSelect.Name("SomeName") property if you are using it more than once. "Name" sets not only the html name, but the id as well, and you never want two ids with the same identifier. So in my code, I am appending a unique Id to my MultiSelect.Name property to ensure a unique id. I am putting the MultiSelect in each row of a table of people. I am showing this to make sure you are using the DataValueField property so you are able to get the selected values (not the text you see in the ui). If you are just showing a list of text values with no id behind them, perhaps that is why you are getting the wrong data?
#foreach (var cm in Model.CaseMembers)
{
<tr>
<td>
#(Html.Kendo().MultiSelect()
.Name("IsDelegateFor" + cm.CaseMemberId)
.Placeholder("is a delegate for..")
.DataTextField("FullName")
.DataValueField("CaseMemberId")
.BindTo(Model.Attorneys)
)
</td>
</tr>
}
then, later on, in my jQuery where I attempt to extract out the DataValueField (CaseMemberId), which is the array of selected values of the MultiSelect...
var sRows = [];
$('#cmGrid tr').each(function () {
// 'this' is a tr
$tr = $(this);
// create an object that will hold my array of selected values (and other stuff)
var rec = {};
rec.IsADelegateFor = [];
// loop over all tds in current row
$('td', $tr).each(function (colIndex, col) {
if (colIndex === 3) {
// make sure our MultiSelect exists in this td
if ($(this).find("#IsDelegateFor" + rec.CaseMemberId).length) {
// it exists, so grab the array of selected ids and assign to our record array
rec.IsADelegateFor = $(this).find("#IsDelegateFor" + rec.CaseMemberId).data("kendoMultiSelect").value();
}
}
}
// add this tr to the collection
sRows.push(rec);
}
so this is all a super verbose way of saying that this single line, as the other people mentioned works perfectly to grab the ids. There is no need to iterate over the .value() array and push the contents to another array!
rec.IsADelegateFor = $(this).find("#IsDelegateFor" + rec.CaseMemberId).data("kendoMultiSelect").value();
So in your original code, there is no reason the following should not work,
var multiselect = $("#SelectRoles").data("kendoMultiSelect");
var selectedData = [];
selectedData = multiselect.value();
console.log(selectedData);
unless
you don't have your MultiSelect set up properly in C# with DataValueField
you have multiple MultiSelects on the page with the exact same id and it's reading from a different one than you think.
You don't even have value fields, just a list of text.
var selected = $("#multi").data("kendoMultiSelect").value();
The solution given by volvox works.
Below is jquery version,
var multiselect = $("#SelectRoles").data("kendoMultiSelect");
var selectedData= [];
var items = multiselect.value();
$.each(items ,function(i,v){
selectedData.push(v);
});

XPathResult and invalid iterator state

I have a relatively large (500-100 rows) HTML table with a bunch of <a> elements. I would like to add a <select> to the top of the page, and populate it by creating an <option> for each <a> in the table.
My first approach looked something like this:
var initSelect = function () {
var select = document.getElementById('mySelect');
var items = document.evaluate('//a', document, null, XPathResult.ANY_TYPE, null);
var item = items.iterateNext();
while (item) {
var elem = document.createElement("option");
var val = document.createAttribute("value");
val.value = elem.nodeValue;
elem.setAttributeNode(val);
elem.innerHTML = item.innerHTML;
select.appendChild(elem);
item = items.iterateNext();
}
};
window.onload = initSelect;
As soon as I tried to appendChild() to the <select> I got an UncaughtInvalidStateError. I figured that modifying the DOM was invalidating my XPathResult iterator, so I tried to add all of the <option> elements to an array first, and then appending them after iterating through all of the results.
var initSelect = function () {
var select = document.getElementById('src_select');
var items = document.evaluate('//a', document, null, XPathResult.ANY_TYPE, null);
var elems = [];
var item = items.iterateNext();
while (item) {
var elem = document.createElement("option");
var val = document.createAttribute("value");
val.value = elem.nodeValue;
elem.setAttributeNode(val);
elem.innerHTML = item.innerHTML;
elems.push(elem);
item = items.iterateNext();
}
for (var i = 0; i < elems.length; i++) {
select.appendChild(elems[i]);
}
};
window.onload = initSelect;
If I step through the code in the debugger, I see items.invalidIteratorState go to true after executing the elem.innerHTML = item.innerHTML line. Then I get the same error on the next call to items.iterateNext().
The first thing I'd like to get working is to just see the <select> populated. After that, the goal is to be able to select an element in the drop down, and have the page navigate to the same link that the corresponding <a> element would have taken me to.
This is the first JavaScript I've written, so I appreciate any and all feedback. At this point, I'm looking for a pure JavaScript solution. Once I get it working I'm going to try to pull JQuery in and revise it.
document.links gives you all a href elements in the document so there is no need to use the XPath API to access those elements. And if you are looking for elements in a particular parent then use e.g. document.getElementById('foo').getElementsByTagName('a') to find all a elements in that element with id attribute being foo. I don't see why you would need the DOM Level 3 XPath API for that, which is not supported in IE anyways. And neither document.links nor the result of getElementsByTagName can be invalidated like an XPath iterator result.
If you really want to use the XPath API then try a snapshot as the result type, it should not fail the way the iterator fails due to document manipulation.

How do I make a reference to the selected value of a "select" list when the "options" are not static?

I have a simple question. I have a select list like this:
var myarray = ["one", "two", "three"];
var container = document.createElement("select");
for (var i = 0; i < myarray.length; i++) {
var element = document.createElement("option");
var textlabel = document.createTextNode(myarray[i]);
if (element.nodeValue == "two") {
element.selected = true;
}
element.appendChild(textlabel);
container.appendChild(element);
}
document.body.appendChild(container);
I have two questions about it:
1) I am pretty sure that the element that should be selected right now is "two"... isn't it?
2) Since the option elements are being created dinamically inside a loop (there are no three different option variables for me to play with, but just one that gets renewed as the loop goes forward), how do I reference the selected one for future uses?
For example, imagine that later on I get user input, and according to that input I want that this list has as a selected item, "three".
Thank you for any help! Here is the fiddle if you want to use it...
1) I am pretty sure that the element that should be selected right now
is "two"... isn't it?
No, it's not: you check element.nodeValue, while in fact you should've been checking textLabel's one - or just the content itself:
if (myarray[i] === 'two') {
element.selected = true;
}
2) Since the option elements are being created dinamically inside a
loop (there are no three different option variables for me to play
with, but just one that gets renewed as the loop goes forward), how do
I reference the selected one for future uses?
See, <select> elements has two useful properties: options (which contains all the options in it, and is updated dynamically) and selectedIndex. You can combine them to get the selected option:
container.addEventListener('change', function() {
console.log(this.options[this.selectedIndex]);
}, false);
But if what you want is to know the value of selected element, that's even easier - with container.value.
For example, imagine that later on I get user input, and according to
that input I want that this list has as a selected item, "three".
That's piece of cake if you know the position of the option that corresponds to this: just use selectedIndex property again:
container.selectedIndex = 3;
Just change the following in the for loop to fix the selection problem:
if (myarray[i] == "two")
Try to use console.log (on chrome or firefox with firebug) to debug your script :
Try this :
var myarray = ["one", "two", "three"];
var container = document.createElement("select");
container.id = "mySelect" ;
for (var i = 0; i < myarray.length; i++) {
var element = document.createElement("option");
var textlabel = document.createTextNode(myarray[i]);
element.appendChild(textlabel);
if (element.value == "two") {
element.selected = true;
}
container.appendChild(element);
}
document.body.appendChild(container);
in order to refer to your selected element you should give your select element an id and access to it like below:
el = document.getElementById('mySelect');
el.selectedIndex ; // give you the selected index
el.options[el.selectedIndex]; // give you the value

Categories