Disable button unless Selected from Typeahead Autocomplete - javascript

--- EDIT: Explaining the Flow ---
In the input field, the user is supposed to enter any string which is basically the name of a company they are searching for. Once the user has entered 3 or more characters, an AJAX call goes to the database, and fetches results matching the users query and displays them like search suggestions in Google or your browser URL bar. One the user selects an item from the suggestions, and clicks on the button, a function called setCompany() is triggered which performs different actions.
What I want is that the button which triggers further actions based on the search query, should be disabled UNTIL the user selects an item from the suggestions that Bloodhound, Typeahead has come up with.
--- End EDIT ---
I am using the code below to enable user to select values from the dropdown list that is generated by Typeahead, Bloodhound.
$(document).ready(function() {
//Set up "Bloodhound" Options
var my_Suggestion_class = new Bloodhound({
datumTokenizer: Bloodhound.tokenizers.obj.whitespace('keyword'),
queryTokenizer: Bloodhound.tokenizers.whitespace,
remote: {
url: "{{ URL::to('user/company/%compquery') }}",
filter: function(x) {
return $.map(x, function(item) {
return {keyword: item['name']};
});
},
wildcard: "%compquery"
}
});
// Initialize Typeahead with Parameters
my_Suggestion_class.initialize();
var typeahead_elem = $('.typeahead');
typeahead_elem.typeahead({
hint: false,
highlight: true,
minLength: 3
},
{
// `ttAdapter` wraps the suggestion engine in an adapter that
// is compatible with the typeahead jQuery plugin
name: 'results',
displayKey: 'keyword',
source: my_Suggestion_class.ttAdapter(),
templates: {
empty: 'No Results'
}
});
});
Here is the HTML:
<div class="iner-sub-header" style="border-bottom: 1px solid #ccc;">
<div class="row" style = "background-color: white;">
<div class="col-md-12 heading">
<div id = "results" class="col-md-12 col-xs-12 results">
<span class="glyphicon glyphicon-search span-search" aria-hidden="true"></span>
<input type="search" class="typeahead custom-input-padding" placeholder="Search Company" id="keyword" onselect="setCompany();">
<button class="btn go-btn" id="go" onclick="setCompany()">SEARCH</button>
</div>
</div>
</div>
</div>
I want to make sure that the button that will trigger setCompany() funtion is disabled until the user selects something from the dropdown that is generated by Typeahead.
Can anyone please help me out?

Try
html
<button class="btn go-btn" id="go" onclick="setCompany()" disabled="true">SEARCH</button>
js
typeahead_elem.bind("typeahead:select", function(ev, suggestion) {
$("#go").prop("disabled", false);
});
See disabled , typeahead.js - Custom Events

This made it work for me. Now its working perfectly.
$(document).ready(function() {
//Set up "Bloodhound" Options
var my_Suggestion_class = new Bloodhound({
datumTokenizer: Bloodhound.tokenizers.obj.whitespace('keyword'),
queryTokenizer: Bloodhound.tokenizers.whitespace,
remote: {
url: "{{ URL::to('user/company/%compquery') }}",
filter: function(x) {
return $.map(x, function(item) {
return {keyword: item['name']};
});
},
wildcard: "%compquery"
}
});
// Initialize Typeahead with Parameters
my_Suggestion_class.initialize();
var typeahead_elem = $('.typeahead');
typeahead_elem.typeahead({
hint: false,
highlight: true,
minLength: 3
},
{
// `ttAdapter` wraps the suggestion engine in an adapter that
// is compatible with the typeahead jQuery plugin
name: 'results',
displayKey: 'keyword',
source: my_Suggestion_class.ttAdapter(),
templates: {
empty: 'No Results'
}
}).on("typeahead:selected typeahead:autocompleted", function(ev, my_Suggestion_class) {
$("#go").prop("disabled", false);
});
});

I wanted a way to re-disable the button if the button if the textbox's content changes and isn't a typeahead selected value. On typeahead:select I store the display text. On every key up I ensure the text still matches the stored variable. Otherwise I disable the button again.
this.searchbox = $('.js-patient-search');
this.searchbox.typeahead({
hint: true
}, {
source: Bloodhound,
display: function(obj) {
return obj.first_name + ' ' + obj.last_name;
}
});
this.searchbox.on('typeahead:select', (function(_this) {
return function(e, suggestion) {
_this.displayName = _this.searchbox.val();
return $('.js-add-encounter').prop('disabled', false);
};
})(this));
return this.searchbox.keyup((function(_this) {
return function(e) {
if (_this.searchbox.val() !== _this.displayName) {
return $('.js-add-encounter').prop('disabled', true);
}
};
})(this));
NOTE: This is parsed coffeescript but I think it should be fairly clear what's going on.

Related

Select field is not showing the dropdown values

Can anyone please help me, I am stuck with this past two days. I am new to Knockoutjs / viewmodel. I am trying to understand how bind the data to the dropdownlist. The dropdown values needs to be pulled from the DB through the API depending on the value entered in another field (which is basically the input parameter for the API to return the dropdown values). The API to return the data is like below
[HttpPost]
public JsonResult GetGInfo(string sNumber)
{
try
{
DSRepository dsr = new DSRepository();
List<String> gTypeList = dsr.GetDDInfo(sNumber);
if (gTypeList != null)
return Json(gTypeList);
else
return null;
}
catch (Exception e)
{
return null;
}}
Below are the two fields
// this value should be passed in to the API to retrieve the dropdown list
self.sNumber= ko.observable().extend({ required: { params: true, message: "Required!" } });
//Dropdown list field
self.gType= ko.observable().extend({ required: true });
//function for making a call to the API
self.getGTypes = function (data, event) {
$.ajax({
url: '/REQ/GetGInfo',
type: 'POST',
data: {
sNumber: self.sNumber()
},
success: function (response) {
if (response.length < 1)
console.log("Record retrieved successfully");
},
error: function (errorThrown) {
console.log("Error retrieving the record");
}
})
};
And UI is like below
<div class="form-group required">
<label for="SNumber" class="control-label">SNumber:</label>
<input type="number" id="SNumber" class="form-control" data-bind="event: {change: getGTypes}, value: sNumber">
</div>
<div class="form-group required">
<label for="GType" class="control-label">GType</label>
<select id="GType" name="GType" class="form-control" data-bind="options: getGTypes, value: gType, optionsCaption: 'Select'"></select>
</div>
</div>
So when the value is entered in the SNumber field the getGenoTypes is called I see that the data is returned from the API through the debugging, for the number I entered I see that below data gTypeList is returned back from API
But in the dropdown I see nothing
Please help me what is that I am missing here totally stuck
You need to actually save the dropdown values that you receive from the API on your viewmodel. You can't just execute the API call and expect Knockout to magically understand it needs to use the (asynchronous) response data to populate the select list.
So basically, you need to do something like this:
// this value should be passed in to the API to retrieve the dropdown list
self.sNumber = ko.observable().extend({ required: { params: true, message: "Required!" } });
//Dropdown list field
self.gType = ko.observable().extend({ required: true });
// Dropdown list values
self.gTypes = ko.observableArray();
//function for making a call to the API
self.getGTypes = function (data, event) {
$.ajax({
url: '/REQ/GetGInfo',
type: 'POST',
data: {
sNumber: self.sNumber()
},
success: function (response) {
self.gTypes(response);
},
error: function (errorThrown) {
console.log("Error retrieving the record");
}
})
};
<select id="GType" name="GType" class="form-control" data-bind="
options: gTypes,
value: gType,
optionsCaption: 'Select'"></select>
Note that I don't know what response looks like so this is probably not 100% correct, but I hope you get the idea of it.

How to use select2 with multiple options using Razor and MVC

I am trying to create a multiple choice list using Select2, Razor and the MVC framework. My problem is that the object in the controller that receives the array input is always null. The front-end looks as follows:
<form class="form-horizontal" method="post" action="#Url.Action(MVC.Configurazione.Contatori.Edit())">
<div class="form-group">
<div class="col-lg-8">
<select class="form-control attributoSelect2" name="attributiSelezionati" value="#Model.AttributiSelezionati">
<option value="#Model.AttributiSelezionati" selected>#Model.AttributoDescrizione</option>
</select>
</div>
</div>
</form>
The action method "Edit", is the controller method that receives the array of chosen items from the drop-down list.
The Javascript is the following:
$('.attributoSelect2').select2({
placeholder: "Search attribute",
multiple: true,
allowClear: true,
minimumInputLength: 0,
ajax: {
dataType: 'json',
delay: 150,
url: "#Url.Action(MVC.Configurazione.Attributi.SearchAttrubutes())",
data: function (params) {
return {
search: params.term
};
},
processResults: function (data) {
return {
results: data.map(function (item) {
return {
id: item.Id,
text: item.Description
};
})
};
}
}
});
And finally the C# controller has an object that is expected to retrieve the data from the view and is defined:
public string[] AttributiSelezionati { get; set; }
and the HttpPost method that receives the data is:
[HttpPost]
public virtual ActionResult Edit(EditViewModel model) { }
Could someone give me some insight into what I am doing wrong and the areas that I should change in order to find the problem?
you class name error not attributoSelect2 is attributesSelect2 , I also make this mistake often. haha
<select class="form-control attributoSelect2" name="attributiSelezionati" value="#Model.AttributiSelezionati">
<option value="#Model.AttributiSelezionati" selected>#Model.AttributoDescrizione</option>
</select>
There are multiple reason for not being receiving data on server. First of all you need to change your select code as follow
#Html.DropDownList("attributiSelezionati", Model.AttributiSelezionati, new { #class = "form-control attributo select2" })
now go to console in browser and get the data of element to confirm that your code properly works in HTML & JS
After that you need to add attribute at your controller's action method as
[OverrideAuthorization]
[HttpPost]
You can try the following approach that has been used in some of our projects without any problem:
View:
#Html.DropDownListFor(m => m.StudentId, Enumerable.Empty<SelectListItem>(), "Select")
$(document).ready(function () {
var student = $("#StudentId");
//for Select2 Options: https://select2.github.io/options.html
student.select2({
language: "tr",//don't forget to add language script (select2/js/i18n/tr.js)
minimumInputLength: 0, //for listing all records > set 0
maximumInputLength: 20, //only allow terms up to 20 characters long
multiple: false,
placeholder: "Select",
allowClear: true,
tags: false, //prevent free text entry
width: "100%",
ajax: {
url: '/Grade/StudentLookup',
dataType: 'json',
delay: 250,
data: function (params) {
return {
query: params.term, //search term
page: params.page
};
},
processResults: function (data, page) {
var newData = [];
$.each(data, function (index, item) {
newData.push({
//id part present in data
id: item.Id,
//string to be displayed
text: item.Name + " " + item.Surname
});
});
return { results: newData };
},
cache: true
},
escapeMarkup: function (markup) { return markup; }
});
//You can simply listen to the select2:select event to get the selected item
student.on('select2:select', onSelect)
function onSelect(evt) {
console.log($(this).val());
}
//Event example for close event
student.on('select2:close', onClose)
function onClose(evt) {
console.log('Closed…');
}
});
Controller:
public ActionResult StudentLookup(string query)
{
var students = repository.Students.Select(m => new StudentViewModel
{
Id = m.Id,
Name = m.Name,
Surname = m.Surname
})
//if "query" is null, get all records
.Where(m => string.IsNullOrEmpty(query) || m.Name.StartsWith(query))
.OrderBy(m => m.Name);
return Json(students, JsonRequestBehavior.AllowGet);
}
Hope this helps...
Update:
Dropdown option groups:
<select>
<optgroup label="Group Name">
<option>Nested option</option>
</optgroup>
</select>
For more information have a look at https://select2.org/options.

itemAdded event not firing on bootstrap taginput with typeahead

I have the following html:
<div class="form-group">
<label><i class="fa fa-university" aria-hidden="true"></i> University</label>
<input id="university" type="text" class="form-control input-sm" placeholder="Search">
</div>
I initialise taginput with typeahead using ajax prefix like this in my script.js file:
// When page loads
$(document).ready(function()
{
// Helper function to initialise tag input with type ahead bloodhound
function initTagInput(inputTarget) {
var targetField = $('input#'+ inputTarget);
targetField.tagsinput({
typeaheadjs: {
name: inputTarget,
displayKey: 'name',
valueKey: 'name',
source: new Bloodhound({
datumTokenizer: Bloodhound.tokenizers.obj.whitespace('name'),
queryTokenizer: Bloodhound.tokenizers.whitespace,
prefetch: {
url: '/ajax/load-dropdown-options/'+ inputTarget,
filter: function(list) {
return $.map(list, function(optionName) {
return { name: optionName };
});
}
}
}).ttAdapter()
},
freeInput: false
});
targetField.on('itemAdded', initSearch);
}
// Initialise tag input fields
initTagInput('university');
// todo other fields
// Method to init search (triggered by tag input)
function initSearch()
{
console.log('todo search');
}
});
This works to the point the taginput with typeahead working together:
==>
As you can see, this has worked. However, the itemAdded event is not firing as the console is empty (as per above test):
Any idea what might be wrong? is this not working because I am initializing it within a helper function?

Type-ahead Bloodhound sort return values.

I am using Twitter type ahead but finding a difficultly to divide the results from mysql query.
My json output looks like this: {"id":"1","name":"Bravo"}
in the current state the results in typeahead are showing the name and the id , I would like to be able to show only the name but the actual submit value of the input to be the id. My script is the flowing:
<script type="text/javascript">
// Instantiate the Bloodhound suggestion engine
var suggestions = new Bloodhound({
datumTokenizer: function (datum) {
return Bloodhound.tokenizers.whitespace(datum.value);
},
queryTokenizer: Bloodhound.tokenizers.whitespace,
remote: {
url: 'includes/livesearch.php?key=%QUERY',
wildcard: '%QUERY',
filter: function (name) {
// Map the remote source JSON array to a JavaScript object array
return $.map(name, function (name) {
return {
value: name
};
});
}
}
});
// Initialize the Bloodhound suggestion engine
suggestions.initialize();
// Instantiate the Typeahead UI
$('.typeahead').typeahead({
hint: true,
minLength: 2
}, {
limit: 7,
displayKey: 'value',
source: suggestions.ttAdapter(),
});
</script>
Any help or suggestions how I can achieve this are very welcome.
Thank you!
You can create an object to store the current retrieved values. At filter option of Bloodhound set the object property to name.name, and value to name.id.
To return and display only name property of retrieved JSON, use the index parameter of $.map() to check the property name of the object. If the property is "name" return {value:name}, else return null.
Use typeahead:selected event to set the value of an <input type="hidden"> element within the <form> using the current value of .typeahead input as a property reference at the previously stored object of initial return value at filter. Set variable reference which stores values to an empty object.
<form>
<input class="typeahead" type="text" placeholder="search">
<input type="hidden" name="result" value="" />
<input type="submit">
</form>
$(function() {
// Instantiate the Bloodhound suggestion engine
var curr = {};
var suggestions = new Bloodhound({
datumTokenizer: function(datum) {
return Bloodhound.tokenizers.whitespace(datum.value);
},
queryTokenizer: Bloodhound.tokenizers.whitespace,
remote: {
url: 'includes/livesearch.php?key=%QUERY',
wildcard: '%QUERY',
filter: function (name) {
curr[name.name] = name.id;
// Map the remote source JSON array to a JavaScript object array
return $.map(name, function (name, index) {
return index === "name" ? {
value: name
} : null;
});
}
}
});
// Initialize the Bloodhound suggestion engine
suggestions.initialize();
// Instantiate the Typeahead UI
$(".typeahead").typeahead({
hint: true,
minLength: 2
}, {
limit: 7,
displayKey: 'value',
source: suggestions.ttAdapter(),
})
.on("typeahead:selected", function (e, datum) {
$("form [name=result]").val(curr[datum.value]); // set value here
curr = {};
});
})
plnkr http://plnkr.co/edit/PJjzxemAQ9fO3P5YBfXi?p=preview

Select2 can't change value

I am dynamically loading a Select2 input with Ajax. Everything works fine, however, when I try to select a different value, It won't change for some reason. Here's an example of my problem: https://gyazo.com/f9ad7c3ead5fcd1d62740cc44f8d9691
As you can see, the value doesn't change when I click on it. Why does this happen? Maybe it helps when I say that both the first value and the other value have an ID of 1 (its data from different tables in the database) but different texts... How can I make it work?
$('.partnersupplierselect').select2({
ajax: {
dataType: "json",
type: "POST",
data: function (params) {
var group = $(this).parent().parent();
var choice = group.find('.partnersupplier:radio:checked').val();
return {
term: params.term,
'_token': token,
'choice': choice
};
},
url: '{{asset('logs/create/bmi/getpartnerssuppliers')}}',
cache: true,
processResults: function (data) {
return {
results: data
};
}
},
"language": {
"noResults": function () {
return "Geen partners / leveranciers gevonden.";
}
},
escapeMarkup: function (markup) {
return markup;
}
});
$('.partnersupplier').on('change', function(){
var group = $(this).parent().parent();
group.find('.partnersupplierselect').select2('val', '');
group.find('.partnersupplierselect').select2('data', null);
});
Here's the HTML, but that shouldn't be the problem. But in case someone wants to see it:
<div class="group">
<label class="mdl-radio mdl-js-radio mdl-js-ripple-effect" for="partner">
{{Form::radio('partnersupplier', 'partner', true, array('class' => 'mdl-radio__button partnersupplier', 'id' => 'partner'))}}
<span class="mdl-radio__label">Test1 </span>
</label>
<label class="mdl-radio mdl-js-radio mdl-js-ripple-effect margin-radio" for="supplier">
{{Form::radio('partnersupplier', 'supplier', false, array('class' => 'mdl-radio__button partnersupplier', 'id' => 'supplier'))}}
<span class="mdl-radio__label">Test2 </span>
</label>
<div class="form-group selectdiv" >
<label for="yearlypartnersuppliermaintainance">Blablabla<br></label>
<select id="yearlypartnersuppliermaintainance" name="yearlypartnersuppliermaintainance" class="searchselect searchselectstyle partnersupplierselect">
</select>
</div>
</div>
I figured it out!
I changed:
$('.partnersupplier').on('change', function(){
var group = $(this).parent().parent();
group.find('.partnersupplierselect').select2('val', '');
group.find('.partnersupplierselect').select2('data', null);
});
to:
$('.partnersupplier').on('change', function(){
var group = $(this).parent().parent();
group.find('.partnersupplierselect').empty().trigger('change');
});
For some reason, this works and the first thing doesn't. Weird, but at least I got it working!

Categories

Resources