Dynamically populate a form field based on a previous user entry - javascript

I have an application that is using Flask and wtforms and part of the functionality is to take user input for the first two fields and populate the remainder of the form fields which are of type SelectMultipleField (I'm going to refer to these as select fields) with choices from a database based on the first two fields (I'm going to refer to these as entry fields).
My issue right now is getting the select fields to dynamically populate. I found a solution here and this seems to be exactly what I need. It instantiates the select fields to all possible choices and then when it detects a JQuery "onchange" event in the entry fields, filters the select fields to choices based on the user entry for the entry fields. An example would be a user entering a specific company into the form and the select fields populating with "locations" only for that company.
However, in adapting this solution to my problem, I have not been able to get the code to run and I have researched far and wide and unable to resolve this. I'm new to both JQuery and Stack Overflow so any help would be greatly appreciated. Below is my code. Note that I am only focusing on one of the entry fields and dynamically populating just one of the select fields until I get this to work. Test_table is the entry field and test_join_key is the select field.
Here's the form with relevant fields-
class QaForm(FlaskForm):
test_table_in = StringField('Test Table', validators=[DataRequired()], id= 'test_table')
test_join_key = SelectMultipleField("Select Test Fields to Join on", choices=[], coerce=str, id = 'select_test_join_key')
Flask view to instantiate all the select fields -
#app.route('/', methods = ['GET', 'POST'])
#app.route('/home', methods = ['GET', 'POST'])
def home():
form = QaForm()
fields_query = f"""select column_name AS Fields from information_schema.columns group by 1;"""
conn.execute(fields_query)
result = conn.fetchall()
select_choices = [(column, column) for column in result]
form.test_join_key.choices = select_choices
Flask view to get choices for select fields based on user input for entry field -
#app.route('/_get_fields/<table>')
def _get_fields(table):
table = request.args.get(table, type=str)
fields_query = f"""select column_name AS Fields from information_schema.columns WHERE table_name = '{table}' group by 1;"""
conn.execute(fields_query)
result = conn.fetchall()
select_choices = [(column, column) for column in result]
return jsonify(select_choices)
JQuery to detect input in entry field and filter choices for select field (injected in HTML file)-
<script charset="utf-8" type="text/javascript">
$function() {
var dropdown = {
test_table: $('#test_table')
test_join_key: $('#select_test_join_key')
}
updateFields();
function updateFields() {
var send = {
test_table: dropdown.test_table.val()
};
dropdown.test_join_key.attr('disabled', 'disabled');
dropdown.test_join_key.empty();
$.getJSON("{{url_for('_get_fields') }}", send, function(data) {
data.forEach(function(item) {
dropdown.test_join_key.append(
$('<option>', {
value: item[0],
text: item[1]
})
);
});
dropdown.test_join_key.removeAttr('disabled');
});
}
dropdown.test_table.on('change', function() {
updateFields();
});
});
</script>
EDIT: Using #Ibsn suggestions, I was able to get the JQuery snippet to run for one form field. However, updating it to perform the same actions for multiple fields using parameters for the function again results in the code not running. I've checked to make sure my syntax is correct based on the tutorial on W3 schools as well as other Stack Overflow questions but still unable to get it to run. Here's the updated Jquery to detect input in entry fields and filter choices for select fields -
<script charset="utf-8" type="text/javascript">
$(function() {
var tables = {
test_table: $('#test_table'),
prod_table: $('#prod_table')
};
var fields = {
test_join_key: $('#select_test_join_key'),
prod_join_key: $('#select_prod_join_key'),
test_dimensions: $('#select_test_dimensions'),
prod_dimensions: $('#select_prod_dimensions'),
test_measures: $('#select_test_measures'),
prod_measures: $('#select_prod_measures')
};
updateFields(table, field);
function updateFields(table, field) {
var send = {
table: tables.table.val()
};
fields.field.attr('disabled', 'disabled');
fields.field.empty();
$.getJSON("{{url_for('_get_fields') }}", send, function(data) {
data.forEach(function(item) {
fields.field.append(
$('<option>', {
value: item[1],
text: item[0]
})
);
});
fields.field.removeAttr('disabled');
});
}
tables.test_table.on('change', function() {
updateFields(tables.test_table, fields.test_join_key);
updateFields(tables.test_table, fields.test_dimensions);
updateFields(tables.test_table, fields.test_measures);
});
tables.prod_table.on('change', function() {
updateFields(tables.prod_table, fields.prod_join_key);
updateFields(tables.prod_table, fields.prod_dimensions);
updateFields(tables.prod_table, fields.prod_measures);
});
});

There are a couple of syntax errors in your code.
$function() {} should be $(function(){}). And you're missing the comma between properties on var dropdown = {}
This is the updated version:
<script charset="utf-8" type="text/javascript">
$(function(){
var dropdown = {
test_table: $('#test_table'),
test_join_key: $('#select_test_join_key')
}
updateFields();
function updateFields() {
var send = {
test_table: dropdown.test_table.val()
};
dropdown.test_join_key.attr('disabled', 'disabled');
dropdown.test_join_key.empty();
$.getJSON("{{url_for('_get_fields') }}", send, function(data) {
data.forEach(function(item) {
dropdown.test_join_key.append(
$('<option>', {
value: item[0],
text: item[1]
})
);
});
dropdown.test_join_key.removeAttr('disabled');
});
}
dropdown.test_table.on('change', function() {
updateFields();
});
});
The OP updated the question with new requirements
If I understand correctly, you're trying to update all the test_ fields when test_table changes and all the prod_ fields when prod_table changes.
So this code should do that:
$(function () {
var tables = {
test_table: $('#test_table'),
prod_table: $('#prod_table')
};
// I'm organizing fields in two arrays, test and prod, for simplyfing iterate over each group
var fields = {
test: [$('#select_test_join_key'), $('#select_test_dimensions'), $('#select_test_measures')],
prod: [$('#select_prod_join_key'), $('#select_prod_dimensions'), $('#select_prod_measures')]
};
// This is for updating fields the first time
fields.test.forEach(item => updateFields(tables.test_table, item));
fields.prod.forEach(item => updateFields(tables.prod_table, item));
function updateFields(table, field) {
var send = {
table: table.val()
};
field.attr('disabled', 'disabled');
field.empty();
$.getJSON("{{url_for('_get_fields') }}", send, function (data) {
data.forEach(function (item) {
field.append(
$('<option>', {
value: item[0],
text: item[1]
})
);
});
field.removeAttr('disabled');
});
}
// Test fields and prod fields are two arrays now, so I can simply iterate through them
tables.test_table.on('change', function () {
fields.test.forEach(item => updateFields(tables.test_table, item));
});
tables.prod_table.on('change', function () {
fields.prod.forEach(item => updateFields(tables.prod_table, item));
});
});

Related

Kendo textbox within dropdownlist

is possible to make textbox within the dropdownlist from Kendo? When user won't find on list what he needs, there should be textbox to set manually some value.
dropdown:
#(Html.Kendo().DropDownList()
.Name("Id")
.DataTextField("StringValue")
.DataValueField("Id")
.SelectedIndex(0))
javascript:
$('#AttributeValue_Id').kendoDropDownList({
dataSource: dataSource,
dataTextField: "Text",
dataValueField: "Value",
optionLabel: '#Html.Raw(T("Product.Attribute.SelectValue"))',
dataBound: function () {
$('#AttributeValue_Id').data('kendoDropDownList').select(0);
}
});
Values in this dropdown depend on another dropdown, where we pick attribute and then this dropdown is getting new data dynamically. Somebody know any solution?
Ok I solved this issue. Solution is:
when the element on dropdown filter is not exist, I can write my string value and redirect to AddNew function. Javascript for no data template:
<script id="noDataTemplate" type="text/x-kendo-tmpl">
<div>
Didn't find the element
</div>
<br />
<button class="k-button" onclick="addNew('#: instance.element[0].id #', '#: instance.filterInput.val() #')">Add new value</button>
AddNew function is get widget element and my value. If user will confirm the script will redirect to action from controller sending needed values - productId from Model, value from parameter and attributeDefinitionId value from first dropdown:
function addNew(widgetId, value) {
var widget = $("#" + widgetId).getKendoDropDownList();
var attributeDefinition = $('#AttributeDefinition').data('kendoDropDownList').value();
var dataSource = widget.dataSource;
if (confirm("Are you sure?")) {
$.ajax({
url: '#Url.Action("AddAttributeValue", "Product")' + '?productId=#Model.Id' + '&value=' + value + '&attributeDefinition=' + attributeDefinition,
cache: false,
}).done(function () {
var grid = $("#attributesGrid").data("kendoGrid");
grid.dataSource.read();
});
dataSource.one("sync", function () {
widget.select(dataSource.view().length - 1);
});
dataSource.sync();
}
};
In controller I'm getting these values and insert them to database:
public ActionResult AddAttributeValue(int productId, string value, int attributeDefinition)
{
if (value != null)
{
try
{
var model = attributeValueRepository.Insert(new ProductAttributeValue()
{
IsCustom = true,
StringValue = value,
AttributeDefinitionId = attributeDefinition,
});
productAttributeRepository.Insert(new ProductAttribute()
{
AttributeValueId = model.Id,
ProductId = productId
});
} catch
{
AddErrorFlashMessage(T("Product.Attribute.AttributeValueError"));
return BadRequest();
}
}
return Ok();
}
Edit
Almost forgot, I set also on dropDown noDataTemplate:
$('#AttributeValue_Id').kendoDropDownList({
dataSource: dataSource,
dataTextField: "Text",
dataValueField: "Value",
optionLabel: '#Html.Raw(T("Product.Attribute.SelectValue"))',
filter: "startswith",
noDataTemplate: $("#noDataTemplate").html(),
dataBound: function () {
$('#AttributeValue_Id').data('kendoDropDownList').select(0);
}
});
Thanks everybody for the tips
Might be late for an answer but kendo dropdownlist supports contains filter for exactly this workflow.
<%= Html.Kendo().DropDownList()
.Name("DropDownList")
.Filter(FilterType.Contains);
%>
Ref: Kendo UI Dropdownlist API
Kendo already have feature within Dropdownlist in case user unable to find his choice option. He can add new element manually in dropdownlist. Please find link below
https://demos.telerik.com/kendo-ui/dropdownlist/addnewitem

Call field from another table in select firebase

I have a table categories and a documents. I want to create a new document and associate this document with a category already created.
The category table has the title field.
In the document creation form I want to put the categories already created in a select.
I tried doing this in the code below, but I ended up getting the select a key from the category -L0_xe_FIK9QdfTGhBDG for example, but I wanted the category title.
var documentosRef = firebase.database().ref('documentos');
var categoriasRef = firebase.database().ref('categorias');
var keyDocumento = ""
function initFirebase(){
categoriasRef.on('value', function(data) {
$('#categoria').html('');
for(categoria in data.val()){
option = "<option>"+categoria+"</option>"
$('#categoria').html($('#categoria').html()+option);
}
})
}
Structure:
{ "categorias": {
"L0_xe_FIK9QdfTGhBDG": {
"titulo": "Categorias 1"
},
"-L0a0FPFkXkKb3VNFN0c":{
"titulo": "Categorias 2"
}
}
Let check it out: JavaScript for/in Statement
var categorias = data.val();
for(var key in categorias){
option = "<option>"+categorias[key].titulo +"</option>"
$('#categoria').html($('#categoria').html()+option);
}

How to set search results to pre-selected in select2?

Alright, so I'm trying to use select2's tagging and AJAX functionality in conjunction for my categories selection.
Currently, the user can search for categories to add to their post. One nice thing I noticed is that, select2 won't let you select the same selection twice (it's grayed and nothing happens when you click on it).
Here's my challenge... let's say a user is editing a post and already has categories selected. These categories appear below the select2 search. For example, let's say the user has the category "Life" selected. This will appear below already checked off.
The problem arises though, when the user searches for "Life". They'll be able to select it and add it again. I could just parse the previous selections and negate the selection, but I'd really like to use the built in functionality of select2 for the sake of consistency.
So how do I set incoming AJAX results to be already selected so that they can't be selected again?
Here's a fiddle. Notice how "Experiences" is grayed out when you search for it, but "life" is not even though it's already selected.
http://jsfiddle.net/odLnznct/1/
function formatStuff(stuff) {
if (stuff.loading) return 'Searching...';
markup = "<p>" + stuff.text + "</p>";
return markup;
}
function formatRepoSelection(stuff) {
return stuff.cat_name || $('.js-select').val();
}
function catAdd() {
var addCat = div = clo = label = labelText = null;
cat = $('.js-select').select2('data');
addCat = cat[0].text;
div = $('#categories-cat_name div.checkbox:first');
clo = div.clone();
$('input[type="checkbox"]', clo).val(addCat);
label = $('label', clo);
labelText = label[0].childNodes[label[0].childNodes.length - 1];
labelText.textContent = addCat;
$(div).before(clo);
}
$('.js-select').select2({
tags: true,
ajax: {
url: 'https://api.myjson.com/bins/58uwe',
dataType: 'json',
delay: 250,
data: function (params) {
return {
q: params.term
};
},
processResults: function (data, page) {
var select2Data = $.map(data, function (obj) {
obj.id = obj.cat_id;
obj.text = obj.cat_name;
return obj;
});
return {
results: select2Data
};
},
cache: true
},
escapeMarkup: function (markup) {
return markup;
},
minimumInputLength: 1,
templateResult: formatStuff,
templateSelection: formatRepoSelection
});
$('.js-select').on('select2:select', function () {
catAdd();
});
You can try this.
Since your results could be multiple, you need to set your select element's multiple true to enable it and just add selected option as default value.
You can do it either javascript or html.
<div id='select-test'>
<select class="js-select" style="width:90%;" multiple=true>
<option value="Life" selected=true>Life</option>
<option value="Joy" selected=true>Joy</option>
</select>
</div>

Change DropDownList data with Javascript

I have a page where a user can select if the transaction type is an inter accounts transfer, or a payment.
The model I pass in had two lists.
One is a list of SelectListItem
One is a list of SelectListItem
One of the lists is populated like this:
var entities = new EntityService().GetEntityListByPortfolio();
foreach (var entity in entities.Where(x=>x.EntityTypeId == (int)Constants.EntityTypes.BankAccount))
{
model.BankAccounts.Add(new SelectListItem
{
Value = entity.Id.ToString(CultureInfo.InvariantCulture),
Text = entity.Description
});
}
If the user selects 'Inter account transfer', I need to:
Populate DropdownA with the list from Accounts, and populate DropdownB with the same list of Accounts
If they select "Payment", then I need to change DrowdownB to a list of ThirdParty.
Is there a way, using javascript, to change the list sources, client side?
function changeDisplay() {
var id = $('.cmbType').val();
if (id == 1) // Payment
{
$('.lstSource'). ---- data from Model.ThirdParties
} else {
$('.lstSource'). ---- data from Model.Accounts
}
}
I'd prefer not to do a call back, as I want it to be quick.
You can load the options by jquery Code is Updated
Here is the code
You will get everything about Newton Json at http://json.codeplex.com/
C# CODE
//You need to import Newtonsoft.Json
string jsonA = JsonConvert.SerializeObject(ThirdParties);
//Pass this jsonstring to the view by viewbag to the
Viewbag.jsonStringA = jsonA;
string jsonB = JsonConvert.SerializeObject(Accounts);
//Pass this jsonstring to the view by viewbag to the
Viewbag.jsonStringB = jsonB;
You will get a jsonstring like this
[{"value":"1","text":"option 1"},{"value":"2","text":"option 2"},{"value":"3","text":"option 3"}]
HTML CODE
<button onclick="loadListA();">Load A</button>
<button onclick="loadListB();">Load B</button>
<select name="" id="items">
</select>
JavaScript Code
function option(value,text){
this.val= value;
this.text = text;
}
var listA=[];
var listB=[];
//you just have to fill the listA and listB by razor Code
//#foreach (var item in Model.ThirdParties)
//{
// <text>
// listA.push(new option('#item.Value', '#item.Text'));
// </text>
// }
//#foreach (var item in Model.Accounts)
// {
// <text>
// listA.push(new option('#item.Value', '#item.Text');
// </text>
// }
listA.push(new option(1,"a"));
listA.push(new option(2,"b"));
listA.push(new option(3,"c"));
listB.push(new option(4,"x"));
listB.push(new option(5,"y"));
listB.push(new option(6,"z"));
function loadListA(){
$("#items").empty();
listA.forEach(function(obj) {
$('#items').append( $('<option></option>').val(obj.val).html(obj.text) )
});
}
function loadListB(){
$("#items").empty();
listB.forEach(function(obj) {
$('#items').append( $('<option></option>').val(obj.val).html(obj.text) )
});
}
NEW Javascript Code fpor Json
var listA=[];
var listB=[];
var jsonStringA ='[{"val":"1","text":"option 1"},{"val":"2","text":"option 2"},{"value":"3","text":"option 3"}]';
var jsonStringB ='[{"val":"4","text":"option 4"},{"val":"5","text":"option 5"},{"value":"6","text":"option 6"}]';
//you just have to fill the listA and listB by razor Code
//var jsonStringA = '#Viewbag.jsonStringA';
//var jsonStringB = '#Viewbag.jsonStringB';
listA = JSON.parse(jsonStringA);
listB = JSON.parse(jsonStringB);
function loadListA(){
$("#items").empty();
listA.forEach(function(obj) {
$('#items').append( $('<option></option>').val(obj.val).html(obj.text) )
});
}
function loadListB(){
$("#items").empty();
listB.forEach(function(obj) {
$('#items').append( $('<option></option>').val(obj.val).html(obj.text) )
});
}
Here is the fiddle http://jsfiddle.net/pratbhoir/TF9m5/1/
See the new Jsfiddle for Json http://jsfiddle.net/pratbhoir/TF9m5/3/
ofcourse you can so that
try
var newOption = "<option value='"+"1"+'>Some Text</option>";
$(".lstSource").append(newOption);
or
$(".lstSource").append($("<option value='123'>Some Text</option>");
Or
$('.lstSource').
append($("<option></option>").
attr("value", "123").
text("Some Text"));
Link for reference
B default, I don't think the concept of "data-source" means something in html/javascript
Nevertheless, the solution you're looking for is something like knockoutjs
You'll be able to bind a viewmodel to any html element, then you will be able to change the data source of your DropDownList
see : http://knockoutjs.com/documentation/selectedOptions-binding.html

Bootstrap Multiselect update option list on flow

I use bootstrap multi-select and I want to update options on flow with ajax
To populate on init my multiselect I do
<select name="model" class="multiselect" multiple="multiple">
<? foreach ($sel_models as $mod) { ?>
<option value="<?= $mod ?>" <?= ($mod == $params['model']) ? 'selected' : '' ?>><?= $mod ?></option>
<? } ?>
</select>
then on event I would like to update my option list with the following ajax
I was trying to use the rebuild method but won't fire the drop-down after creation
$.ajax({
type: 'post',
url: "helper/ajax_search.php",
data: {models: decodeURIComponent(brands)},
dataType: 'json',
success: function(data) {
$('select.multiselect').empty();
$('select.multiselect').append(
$('<option></option>')
.text('alle')
.val('alle')
);
$.each(data, function(index, html) {
$('select.multiselect').append(
$('<option></option>')
.text(html.name)
.val(html.name)
);
});
$('.multiselect').multiselect('rebuild')
},
error: function(error) {
console.log("Error:");
console.log(error);
}
});
With firebug I can see that the list is generated but on select won't show up
In the doc I can read :
.multiselect('setOptions', options)
Used to change configuration after initializing the multiselect. This may be useful in combination with .multiselect('rebuild').
Maybe you can't change your widget data by your initial way. In a correct way you should use setOptions method.
Else : With your way, maybe should you think about destroy your widget .multiselect('destroy') and create it again after.
Update after comment :
In the doc : ( you've linked )
Provides data for building the select's options the following way:
var data = [
{label: "ACNP", value: "ACNP"},
{label: "test", value: "test"}
];
$("#multiselect").multiselect('dataprovider', data);
So :
When you get data from your ajax call, you have to create an array of objects ( it's the options in the select you want to have ) with the format like
var data =
[
{label: 'option1Label', value: 'option1Value'},
{label: 'option2Label', value: 'option2Value'},
...
]
When your objects array is created, then you just have to call the method
$("#multiselect").multiselect('dataprovider', data);
Where data is your array of objects.
I hope I'm clear :/
As an alternative to multiselect('dataprovider', data) you can build the list with jquery append exactly the way you did in your question. The only change you need to make is to delay the rebuild until after the ajax request is complete.
var buildDrivers = $.getJSON('resources/orders/drivers.json', function(data) {
$.each(data, function(i, driver) {
$('#toolbar select[name="drivers"]').append('<option>'+driver+'</option>');
});
});
buildDrivers.complete(function() {
$('.multiselect').multiselect('rebuild');
});
see http://api.jquery.com/jquery.getjson/ for documentation
I've been added the functionality of updating options after filtering and getting them from the server side. This solution relays on the concept of injecting new options, destroying the select and initializing it again.
I took into account:
Considering the existing selected options, which must stay.
Removing duplicate options (might be as a conflict from which that already selected and the new that came from the server).
Keeping the options tray open after the update.
Reassign the previous text in the search text box & focusing it.
Just add the 'updateOptions' as a function after the 'refresh' function along with the two helper functions as follows:
updateOptions: function (options) {
var select = this.$select;
options += this.getSelectedOptionsString();
var selectedIds = select.val(),
btnGroup = select.next('.btn-group'),
searchInput = btnGroup.find('.multiselect-search'),
inputVal = searchInput.val();
options = this.removeOptionsDuplications(options);
if (!options) {
options = '<option disabled></option>';
}
// 1) Replacing the options with new & already selected options
select.html(options);
// 2) Destroyng the select
select.multiselect('destroy');
// 3) Reselecting the previously selected values
if (selectedIds) {
select.val(selectedIds);
}
// 4) Initialize the select again after destroying it
select.multiselect(this.options);
btnGroup = select.next('.btn-group');
searchInput = btnGroup.find('.multiselect-search');
// 5) Keep the tray options open
btnGroup.addClass('open');
// 6) Setting the search input again & focusing it
searchInput.val(inputVal);
searchInput.focus();
},
getSelectedOptionsString: function () { // Helper
var result = '',
select = this.$select,
options = select.find('option:selected');
if (options && options.length) {
$.each(options, function (index, value) {
if (value) {
result += value.outerHTML;
}
});
}
return result;
},
removeOptionsDuplications: function (options) { // Helper
var result = '',
ids = new Object();
if (options && options.length) {
options = $(options);
$.each(options, function (index, value) {
var option = $(value),
optionId = option.attr('value');
if (optionId) {
if (!ids[optionId]) {
result += option[0].outerHTML;
ids[optionId] = true;
}
}
});
}
return result;
},
Demo:
State:
"Option 1"
$('#select').multiselect('updateOptions', '<option value="2">Option 2</option>');
State:
"Option 2"
"Option 1"
I think this is an easier way to add options on the fly (using ajax or any other listener) to an existing Bootstrap MultiSelect.
Following is a simplified example to add options:
function addOptionToMultiSelect(multiselectSelector, value, selected) {
var data = [];
$(multiselectSelector + ' option').each(function(){
var value = $(this)[0].value;
var selected = $(this)[0].selected;
data.push({label: value, value: value, selected: selected});
});
// Add the new item
data.push({label: value, value: value, selected: selected});
$(multiselectSelector).multiselect('dataprovider', data);
}
For simplicity, I have assumed both the label and value are the same in an option. Note that the already selected options are taken care of by reading the selected attribute from the existing options. You can make it more sophisticated by tracking the disabled and other attributes.
Sample:
addOptionToMultiSelect('#multiselect-example', 'new-option', true);

Categories