Select2 with custom "text" property name - javascript

I am trying to use Select2 with a different property name for text. I have a array of objects which have name property. I am trying to use that object directly in select2 as given below
HTML
<div id="container">
<input type="hidden" id="selectElement"/>
</div>
Javascript
function format(item) {
return item.name;
};
$("#selectElement").select2({
placeholder: 'Select ...',
data: [{id:0, name: "Home"},{id:1, name: "About Us"},{id:2, name: "Reach Us"}],
formatResult: format,
formatSelection: format,
});
As demonstrated in this Fiddle the options get populated correctly. But when we enter some text matching any of the existing options, the select2 displays No Matches Found in-spite of having a match.
What am I missing here, which results in default matching not working ?

Looks like you also have to redefine matcher function:
$("#selectElement").select2({
placeholder: 'Select ...',
data: [{id:0, name: "Home"},{id:1, name: "About Us"},{id:2, name: "Reach Us"}],
formatResult: format,
formatSelection: format,
matcher: function(term, text, option) {
return option.name.toUpperCase().indexOf(term.toUpperCase())>=0;
}
});
Demo: http://jsfiddle.net/dfsq/hevhuxpo/4/

Related

Select2 Default values for multiple select and allowed tags

I have a select 2 multi-select option
<select multiple name="event_type[]" class="form-control" id="selectEvents">
#foreach ($eTypes as $type)
<option>{{$type}}</option>
#endforeach
</select>
I want to set some default values just in case the user is editing the form.
I have successfully done that by doing this
var s2 = $("#selectEvents").select2({
placeholder: "Choose event type",
tags: true
});
s2.val(["Trade Fair", "CA", "Party"]).trigger("change"); //CA doesn't show as a default
But the problem is i am allowing user generated options using the tags: true option for select2.
When I set a default value that was initially in the html options it works, but when I set a default that was user generated it doesn't work.
It is my first time using select2.
How can i achieve this?
Doing a little digging, I can see this issue raised on GitHub.
One option is to check to see if the value exists, and append it if it doesn't.
var s2 = $("#selectEvents").select2({
placeholder: "Choose event type",
tags: true
});
var vals = ["Trade Fair", "CA", "Party"];
vals.forEach(function(e){
if(!s2.find('option:contains(' + e + ')').length)
s2.append($('<option>').text(e));
});
s2.val(vals).trigger("change");
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.3/js/select2.min.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.3/css/select2.css" rel="stylesheet"/>
<select multiple name="event_type[]" class="form-control" id="selectEvents">
<option>Trade Fair</option>
<option>Party</option>
<option>Foo</option>
<option>Bar</option>
</select>
An other simple way is set value to select option and make it select2
again :
$('#properties').val(["Trade Fair", "CA", "Party"]);
$('#properties').select2({});
It is working for me
Little improvement:
$('#province').val(["3", "4", "5"]).trigger('change');
I recently had to implement a select2 with options 'multiple' and 'tags' in a PHP script, and ran into a similar problem. The documentation said to add any initial selections as html option tags, but when I tried to add two, only one would show up in my select2 control.
I ended up initializing the select2 control with the config object 'data' option, which I would create dynamically.
var initialPropertyOptions = [];
#foreach ($model->properties as $initialProperty`)
var initialPropertyOption = {
id: {{ $initialProperty->id }},
text: '{{ $initialProperty->name }}',
selected: true
}
initialPropertyOptions.push(initialPropertyOption);
#endforeach
$('#properties').select2({
ajax: {
url: route('models.propertySearch'),
dataType: 'json',
delay: 250,
processResults: function(data) {
return {
results: data
}
}
},
placeholder: 'Enter property name',
minimumInputLength: 1,
multiple: true,
tags: true,
data: initialPropertyOptions
});
<div>
<label for="properties">Properties</label>
<select name="properties[]" id="properties">
</select>
</div>

Extract more info from themoviedb using typeahead.js and put it in other inputs

I'm using typeahead.js to get movie info from themoviedb api. I need when the user type the movie's title get the year of the movie and the ID of the movie to be added automatically to other inputs.
So when the user using the input Movie title and he click on the suggested titles, It will automatically add the year and the movie id to the other inputs
HTML Code
<input class="typeahead" placeholder="Movie Title Here"><br>
<input class="year" placeholder="Year Here">
<input class="id" placeholder="Year ID">
JS code
Look close to the return (at Line 12) there is the info I need to be transferred to other inputs
var movies = new Bloodhound({
datumTokenizer: function (datum) {
return Bloodhound.tokenizers.whitespace(datum.value);
},
queryTokenizer: Bloodhound.tokenizers.whitespace,
limit: 10,
remote: {
url: 'http://api.themoviedb.org/3/search/movie?api_key=470fd2ec8853e25d2f8d86f685d2270e&query=%QUERY&search_type=ngram',
filter: function (movies) {
// Map the remote source JSON array to a JavaScript array
return $.map(movies.results, function (movie) {
return {
id: movie.id,
value: movie.original_title,
year: (movie.release_date.substr(0,4) ? movie.release_date.substr(0,4) : '')
};
});
}
}
});
// Initialize the Bloodhound suggestion engine
movies.initialize();
// Instantiate the Typeahead UI
$('.typeahead').typeahead({
hint: true,
highlight: true
}, {
displayKey: 'value',
source: movies.ttAdapter(),
templates: {
empty: [
'<div class="empty-message">',
'unable to find any Best Picture winners that match the current query',
'</div>'
].join('\n'),
suggestion: Handlebars.compile('<p><strong>{{value}}</strong> – {{year}}</p>')
}
});
here is my code in action on jsfiddle to try yourself:
http://jsfiddle.net/Jim_Toth/ss8L24x8/
I've added a way to auto-populate the associated input controls here:
http://jsfiddle.net/Fresh/cmq80qx3/
The key part of the code to achieve the auto-population is:
bind("typeahead:selected", function (obj, datum, name) {
$('.year').val(datum.year);
$('.id').val(datum.id);
});
This code specifies the function which should be called when a typeahead value is selected, in this case it appropriately sets the values of the year and id inputs.

Select box placeholder in HTML Template

Code in controller:
$scope.infoOptions = [
{ name: 'Select Option', value: '0' },
{ name: 'Some Option', value: '1' }
];
HTML:
<select data-ng-model="nothing" data-ng-options="info.name for info in infoOptions ">
</select>
Angular puts that damn empty option at the top/selected by default. I've seen some answers to this question that suggest selecting a default option in the $scope for the form, but this select box is in a template in a dynamic form (ie. can be a number of select boxes). This is really only for demonstration purposes - is there anyway I can get rid of that empty option in a template?

How to submit BackboneJs models in html form post?

I am posting a form to a url from a page that displays backbonejs models.
This is how my backbonejs model looks like in the form:
Form:
<form class="form-horizontal" action="profit" method="post">
<fieldset>
<!-- Form Name -->
<legend>Form Name</legend>
<!-- Button -->
<div class="control-group">
<div class="controls">
<div id="example-1-result" class="backgrid-container"></div>
<button id="profit" name="profit" class="btn btn-primary">Button</button>
</div>
</div>
</fieldset>
</form>
JavaScript:
<!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
<script src="https://code.jquery.com/jquery.js"></script>
<!-- Include all compiled plugins (below), or include individual files as needed -->
<script src="../static/js/bootstrap.min.js"></script>
<script>
var Trade = Backbone.Model.extend({});
var Trades = Backbone.Collection.extend({
model: Trade,
url: "fodata"
});
var columns = [
{
name: "trade_id",
label: "Trade Id",
// The cell type can be a reference of a Backgrid.Cell subclass, any Backgrid.Cell subclass instances like *id* above, or a string
cell: "string" // This is converted to "StringCell" and a corresponding class in the Backgrid package namespace is looked up
},
{
name: "order_id",
label: "Order Id",
// The cell type can be a reference of a Backgrid.Cell subclass, any Backgrid.Cell subclass instances like *id* above, or a string
cell: "string" // This is converted to "StringCell" and a corresponding class in the Backgrid package namespace is looked up
},
{
name: "trade_date",
label: "Trade Date",
cell: "datetime",
},
{
name: "trade_time",
label: "Trade Time",
cell: "datetime",
},
{
name: "contract_description",
label: "Contract Description",
cell: "string" // An integer cell is a number cell that displays humanized integers
},
{
name: "expiry_date",
label: "Expiry Date",
cell: "datetime",
},
{
name: "buy_quantity",
label: "Buy Quantity",
cell: "integer" // An integer cell is a number cell that displays humanized integers
}, {
name: "buy_rate",
label: "Buy Rate",
cell: "number",
},
{
name: "sale_quantity",
label: "Sale Quantity",
cell: "integer" // An integer cell is a number cell that displays humanized integers
}, {
name: "sale_rate",
label: "Sale Rate",
cell: "number",
}
];
var trades = new Trades;
var grid = new Backgrid.Grid({
columns: columns,
collection: trades
});
// Initialize a new Grid instance
var refreshgrid = function(){
$("#example-1-result").prepend(grid.render().$el);
}
</script>
Now, while submitting, I need to submit the trades JSON data but I'm not able to get the model to submit.
How do I achieve this? Do I need to use backbonejs forms?
Backgrid is supposed to give you something to quickly edit all the models you give it to it (through the collection) using the normal backbone mechanisms. So technically, you shouldn't use a form to wrap it, and when editing each Trade model, the Trade model should be saved() after the end of the edit. Backgrid is a nice way to wrap basic CRUD on top of a resource.
If that's not the behavior you want, then maybe Backgrid isn't what you need?
That being said, nothing prevents you at anytime, no matter what's in the form, to do something like:
$("form").on("submit", function(event){
//Don't send the form, stay on the page
event.preventDefault();
//Transform the collection of Trades into an array of JSONified Trade
data = trades.map(function(trade){
return trade.toJSON();
});
//Post this to the server
$.post("url", {trades: data}, ... );
return false;
});
But that seems odd.
If I misunderstood, maybe you can clarify your intent a bit? Hope this helps!
You should just be able to serialize the model and POST it.
See: http://api.jquery.com/serialize/
If you .serialize the data you want and send that as part of your POST request it should work. Alternatively if you're looking for something a bit more advanced take a look at syphon:
http://lostechies.com/derickbailey/2012/05/17/backbone-syphon-serialize-form-inputs-to-javascript-objects/
Serializing is most likely the key to what you want to achieve.

knockout.js - nested array data and cascading pre-populated dropdown lists binding

I'm fairly new to knockout.js, however, I've been happily using it in my ASP.NET MVC 4 project, until I ran into this obstacle which has been bothering me for a while, can't seem to put my finger on it.
The scenario which I'm working on requires several combinations of location data (region, country, city), i.e. cascading dropdown lists, which isn't a problem to do when inputting fresh data, but I ran into problem(s) when trying to edit the saved data.
Data is in JSON format, with nested arrays, looks like this (shortened for illustration purposes):
var newData =
[
{
"ID":1,
"Name":"Australia and New Zealand",
"Countries":[
{
"ID":13,
"Name":"Australia",
"Cities":[
{
"ID":19,
"Name":"Brisbane"
},
{
"ID":28,
"Name":"Cairns"
},
...
I suspect I can't load the data (or more clearly, to bind it) properly since I'm having trouble accessing the Region sub-array (which contains Region's Countries) and the Countries sub-array (which contains Countries' Cities).
Then there's the matter of having prepopulated options, which works partially, the viewmodel loads the number of lines, but doesn't select anything.
Here's the VM:
var existingRows = [
{
"Region": 1,
"Country": 13,
"City": 19
},
{
"Region": 1,
"Country": 158,
"City": 3
}];
var Location = function (region, country, city) {
var self = this;
self.region = ko.observable(region);
self.country = ko.observable(country);
self.city = ko.observable(city);
// Whenever the region changes, reset the country selection
self.region.subscribe(function () {
self.country(undefined);
});
// Whenever the country changes, reset the city selection
self.country.subscribe(function () {
self.city(undefined);
});
};
var LocationViewModel = function (data) {
var self = this;
self.lines = ko.observableArray(ko.utils.arrayMap(data, function (row)
{
var rowRegion = ko.utils.arrayFirst(newData, function (region)
{
return region.ID == row.Region;
});
var rowCountry = ko.utils.arrayFirst(rowRegion.Countries, function (country) {
return country.ID == row.Country;
});
var rowCity = ko.utils.arrayFirst(rowCountry.Cities, function (city) {
return city.ID == row.City;
});
return new Location(rowRegion, rowCountry, rowCity);
}));
// Operations
self.addLine = function () {
self.lines.push(new Location())
};
self.removeLine = function (line) {
self.lines.remove(line)
};
};
var lvm = new LocationViewModel(existingRows);
$(function () {
ko.applyBindings(lvm);
});
HTML code:
<tbody data-bind="foreach: lines">
<tr>
<td><select data-bind="options: newData, optionsText: 'Name', optionsValue: 'ID', optionsCaption: 'Select a region...', attr: { name: 'SubRegionIndex' + '['+$index()+']' }, value: region"></select></td>
<td><select data-bind="options: Countries, optionsText: 'Name', optionsValue: 'ID', optionsCaption: 'Select a country...', attr: { name: 'CountryIndex' + '['+$index()+']' }, value: country"></select></td>
<td><select data-bind="options: Cities, optionsText: 'Name', optionsValue: 'ID', optionsCaption: 'Select a city...', attr: { name: 'CityIndex' + '['+$index()+']' }, value: city"></select></td>
<td><a href='#' data-bind='click: $parent.removeLine'>Remove</a></td>
</tr>
</tbody>
I tried to modify the Cart editor example from the knockout.js website with prepopulated data, but haven't really made much progress, I seem to be missing something. Didn't really find anything with nested arrays so I'm stuck here...
I've put up the full code on JSFiddle here:
http://jsfiddle.net/fgXA2/1/
Any help would be appreciated.
The problem is the way in which you are binding to the selected item in your select lists:
<select data-bind="
options: newData,
optionsText: 'Name',
optionsValue: 'ID',
value: region">
</select>
Here you are binding the ID property from your JSON data to the region property on your view model.
This means that when you bind your second select list:
<td data-bind="with: region">
<select data-bind="
options: Countries,
optionsText: 'Name',
optionsValue: 'ID',
value: $parent.country">
</select>
</td>
You attempt to bind to region.Countries. However, region simply contains the selected region ID. In this case the console is your friend:
Uncaught Error: Unable to parse bindings. Message: ReferenceError:
Countries is not defined;
The same problem is true of your third select list for Cities since you are now attempting to bind to country.Cities where country is also just the ID.
There are two options available here. The first is to remove the optionsValue parameters, thus binding the actual JSON objects to your view model properties. That and a binding error on your Cities select box (you were binding to CityName instead of Name) were the only problems:
http://jsfiddle.net/benfosterdev/wHtRZ/
As you can see from the example I've used the ko.toJSON utility to output your view model's object graph. This can be very useful in resolving problems (in your case you would have seen that the region property was just an number).
The downside of the above approach is that you end up storing a copy of all of the countries, and their cities for the selected country in your view model.
A better solution if dealing with large data sets would be to only store the selected identifier (which I believe you were attempting to do originally) and then define computed properties that filter your single data set for the required values.
An example of this can be seen at http://jsfiddle.net/benfosterdev/Bbbt3, using the following computed properties:
var getById = function (items, id) {
return ko.utils.arrayFirst(items, function (item) {
return item.ID === id;
});
};
this.countries = ko.computed(function () {
var region = getById(this.selectedRegion.regions, this.selectedRegion());
return region ? ko.utils.arrayMap(region.Countries, function (item) {
return {
ID: item.ID,
Name: item.Name
};
}) : [];
}, this);
this.cities = ko.computed(function () {
var region = getById(this.selectedRegion.regions, this.selectedRegion());
if (region) {
var country = getById(region.Countries, this.selectedCountry());
if (country) {
return country.Cities;
}
}
}, this);
You can see from the rendered object graph that only the currently selected countries and cities are copied to the view model.

Categories