Multiselect html


We humans are greedy creatures. When we’re placed in front of a choice to select one of many options, we start complaining — Hey, why can’t I pick two? Why not five? Can’t I have both pineapple AND pepperoni on my pizza?

This is why multiple selection form fields had to be invented. Let’s examine a few examples of how multiple select fields can be implemented in HTML and PHP (as well as some JavaScript).

Traditional HTML/CSS techniques

Checkboxes

The first obvious option that we have is the good old set of checkboxes:

The HTML code behind this is pretty straightforward:

<input id=»cheese» type=»checkbox» name=»ingredients[]» value=»Cheese» /> <label for=»cheese»>Cheese</label> <br /> <input id=»olives» type=»checkbox.


t;Anchovies</label>

You might notice that all the checkboxes share the same name ingredients[]. The square brackets allow us to easily parse the collected form data in PHP. We’ll get to that later.

Lists of checkboxes have the advantage of being simple to implement and easy to use for the end user. But, they do not scale well — as long as there are just a few options, it’s fine, but if you need to let the user select a few choices from dozens of options (a list of countries, for instance), you end up with a really, reeeally long list, and your form gets huge. And if there’s one thing people hate, it’s huge forms.

There are some workarounds for long lists of checkboxes, like placing the checkboxes in several columns:

This can be accomplished with floating divs:

<div class=»container»> <div style=»width: 12em; float: left;»> <input id=»cheese» type=»checkbox» name=»ingredients[]» value=»Cheese» /> … </div> <div style=»width: 12em; float: left;»> <input id=»mushrooms» type=»checkbox» name=»ingredients[]» value=»Mushrooms» /> … </div> </div>

Another workaround is to put all the checkboxes in a scrollable div:

For this variant, all we need to do is give the


div some dimensions and set its CSS overflow property to auto:

<div style=»height: 6em; width: 12em; overflow: auto;»> <input id=»cheese» type=»checkbox» name=»ingredients[]» value=»Cheese» /> <label for=»cheese»>Cheese</label> <br /> … </div>

With the scrollable list, we can keep the form small even with a hundred options. But (there’s always a but), this solution has one major drawback — the user can’t see all the already checked options at once without scrolling the box.

Multiple selection list box

There is a HTML element specifically designed for multiple selection. The select element, usually used to produce a drop down list, can be transformed into a multiple selection box with the addition of the multiple attribute:

Here’s the code:


<select name=»ingredients[]» multiple=»multiple» size=»5″> <option value=»Cheese»>Cheese</option> <option value=»Olives»>Olives</option> <option value=»Pepperoni»>Pepperoni</option> … </select>

Apparently, this method suffers from the same problem as the scrollable checkbox list — you need to scroll the box to see all your choices. Oh well.

Fancy JavaScript method

In light of the above, the optimal solution should be both compact and let us see all the selected options without scrolling. We can’t accomplish this with just HTML and CSS, but, hey, we have JavaScript, right?

Let’s use our imagination here. To keep the list of options condensed, we can use a drop down box. Everytime the user selects an option, we’ll do some JavaScript-dynamic-HTML work to add the selected option to a list placed above the box. This way, all our choices will be visible at the same time.

Can you picture it? If not, don’t worry, here’s a working example:

We’re also able to remove the already selected options by clicking them on the list. Oh, and one more thing — when adding an option, we make sure it’s not already listed (so that we don’t end up with too much bacon on our pizza).

With some CSS styling and an image here and there, our solution can get a more attractive look:


Nice, isn’t it? Here’s the HTML:

<ul> <li onclick=»this.parentNode.removeChild(this);»> <input type=»hidden» name=»ingredients[]» value=»Cheese» /> Cheese </li> <li onclick=»this.parentNode.removeChild(this);»> <input type=»hidden» name=»ingredients[]» value=»Ham» /> Ham </li> <li onclick=»this.parentNode.removeChild(this);»> <input type=»hidden» name=»ingredients[]» value=»Mushrooms» /> Mushrooms </li> </ul> <select onchange=»selectIngredient(this);»> <option value=»Cheese»>Cheese</option> <option value=»Olives»>Olives</option> <option value=»Pepperoni»>Pepperoni</option> … </select>

The structure here is quite simple — an unordered list and a drop down select field. One thing worth noting is how we store the selected values to have them sent to the server when the form is submitted. For this purpose, inside every list item there’s a hidden input element with the appropriate value.

All the action happens in the JavaScript function selectIngredient:

function selectIngredient(select) { var option = select.o.


[]’; input.value = option.value; li.appendChild(input); li.appendChild(text); li.setAttribute(‘onclick’, ‘this.parentNode.removeChild(this);’); ul.appendChild(li); }

If you don’t like dealing directly with DOM, here’s a jQuery equivalent:

function selectIngredient(select) { var $ul = $(select).prev(‘ul’); if ($ul.find(‘input[value=’ + $(select).val() + ‘]’).length == 0) $ul.append(‘<li onclick=»$(this).remove();»>’ + ‘<input type=»hidden» name=»ingredients[]» value=»‘ + $(select).val() + ‘» /> ‘ + $(select).find(‘option[selected]’).text() + ‘</li>’); }

The function checks if the selected option is not already on the list — it does that by looking for a hidden input element with the selected value. If it fails to find one, it creates a new list item for the chosen option (with a new hidden input inside) and appends it to the list.

Note that the hidden input


elements are named ingredients[], just like the checkboxes and the multiple selection list box in the previous examples. This will allow us to use the same PHP code to deal with the submitted data.

Processing results

As I mentioned, using the square brackets in the input/select name makes it easier to process the collected form data in PHP. That’s because it automatically produces an array for us to work with. You can read more about this in the PHP FAQ.

Assuming we get our data via $_POST, we can process each selected option with the following foreach loop:

foreach ($_POST[‘ingredients’] as $ingredient) { // do something with $ingredient }

The «do something» part might be whatever you want to do with $ingredient — store it in a database, save it in a file, or mail it to your local Domino’s Pizza.

Generating options dynamically in PHP

In many (if not most) cases, we will want to automatically generate the list of options based on some data (eg. a set of records retrieved from a database, XML file, or some other source). To simplify, we’ll assume the data is already collected in an array.

Basically, there might be two types of arrays that we’ll be using to produce the options. The first type is when we’re only interested in item names, so we have a non-associative array of strings:


$ingredients = array(‘Cheese’, ‘Olives’, ‘Pepperoni’, …);

The second is when there is some additional information attached to each item, for example a database record ID, and this information is what is really important to us.

$ingredients = array(‘3’ => ‘Cheese’, ‘5’ => ‘Olives’, ‘8’ => ‘Pepperoni’, …);

In both cases, we can generate the HTML code for the list of choices with a simple foreach loop. To produce a set of checkboxes from a non-associative array (the first type), we do the following:

<? foreach ($ingredients as $ingredient) { ?> <input id=»<?= $ingredient ?>» type=»checkbox» name=»ingredients[]» value=»<?= $ingredient ?>» /> <label for=»<?= $ingredient ?>»><?= $ingredient ?></label> <br /> <? } ?>

And here’s the equivalent loop for the associative variant:

<? foreach ($ingredients as $key => $ingredient) { ?> <input id=»<?= $ingredient ?>» type=»checkbox» name=»ingredients[]» value=»<?= $key ?>» /> <label for=»<?= $ingredient ?>»><?= $ingredient ?></label> <br /> <? } ?>

A similar foreach


loop could be used to produce a multiple selection list box, or a drop down list for our JavaScript solution.

Pre-selecting options

We might want to present the user with a set of default, already selected options — say, we notice that the vast majority of our pizza place customers want bacon and green pepper, so we decide to pre-select the two ingredients for them and make their lives easier. We’ll put the default options in an array named $selected:

$selected = array(‘Bacon’, ‘Green Pepper’);

But this is not the only reason why we might want to have some options already selected when the form is displayed. If the user submits a form, and we notice there’s some invalid or missing information, we need to display the form again and ask the user to correct the errors. To save the user the frustration of having to enter everything again, we need to fill the form with all the information already provided — in the case of multiple select fields, we need to pre-select the submitted set of options.

If the form has been submitted, we fill $selected with the submitted options. Additionally, we check if there actually are any submitted options, as the user might have not selected anything. If that’s the case, $_POST['ingredients']


won’t even exist, and we need to make $selected an empty array:

if (isset($_POST[‘make_order’])) { if (isset($_POST[‘ingredients’])) // Submitted selection $selected = $_POST[‘ingredients’]; else // Nothing selected $selected = array(); } else { // Default selection $selected = array(‘Bacon’, ‘Green Pepper’); }

We then proceed with generating the HTML code for the set of options (checkboxes again). For every item, we use the in_array() function to check if the value is present in the $selected array — if it is, we add a checked attribute to the checkbox. With the non-associative array of options, we use the following foreach loop:

<? foreach ($ingredients as $ingredient) { ?> <input id=»<?= $ingredient ?>» type=»checkbox» name=»ingredients[]» value=»<?= $ingredient ?>» <?= in_array($ingredient, $selected) ? ‘selected=»selected»‘ : » ?> /><label for=»<?= $ingredient ?>»><?= $ingredient ?></label> <br /> <? } ?>

With the associative array, the code is pretty much the same, except that we need to check if the key ($key


) is in the $selected array, not the value ($ingredient):

<? foreach ($ingredients as $ingredient) { ?> <input id=»<?= $ingredient ?>» type=»checkbox» name=»ingredients[]» value=»<?= $key ?>» <?= in_array($key, $selected) ? ‘selected=»selected»‘ : » ?> /><label for=»<?= $ingredient ?>»><?= $ingredient ?></label> <br /> <? } ?>

We can generate a multiple selection list box in a similar fashion. Unfortunately, we’ll have a slight problem with our pretty JavaScript method. We surely can generate a list of items with a foreach loop:

<ul> <? foreach ($ingredients as $ingredient) { if (in_array($ingredient, $selected)) { ?> <li onclick=»this.parentNode.removeChild(this);»> <input type=»hidden» name=»ingredients[]» value=»<?= $ingredient ?>» /> Cheese </li> <? } } ?> </ul> <select onchange=»selectIngredient(this);»> … </select>

However, the $selected array might be empty. This would lead to generating an empty <ul> element, which is not valid HTML. We’re good people and we want our HTML to be valid, so we have to do something about it.

We’ll modify our code to check if the $selected array is empty, and if it is, we’ll simply not generate the unordered list at all:

<? if (count($selected) > 0) { ?> <ul> <? foreach ($ingredients as $ingredient) { if (in_array($ingredient, $selected)) { ?> <li onclick=»this.parentNode.removeChild(this);»> <input type=»hidden» name=»ingredients[]» value=»<?= $ingredient ?>» /> Cheese </li> <? } } ?> </ul> <? } ?> <select onchange=»selectIngredient(this);»> … </select>

But that’s not all. If $selected is empty and there’s no <ul>, our precious JavaScript code that adds new items to the list will no longer work (for the simple reason that there would be no list to add to). To fix this, we need to add a few lines of code to the selectIngredient() function to check if the list exists, and if it does not, create it on the fly.

function selectIngredient(select) { var option = select.options[select.selectedIndex]; var ul = select.parentNode.getElementsByTagName(‘ul’)[0]; if (!ul) { ul = document.createElement(«ul»); select.parentNode.insertBefore(ul, select); } var choices = ul.getElementsByTagName(‘input’); … }

Now, we no longer need to fear the empty $selected array.

More to follow. Stay tuned.

odyniec.net

HTML
  <div class="row">   <div class="col-xs-5">   <select name="from[]" id="multi_d" class="form-control" size="26" multiple="multiple">   <option value="1">C++</option>   <option value="2">C#</option>   <option value="3">Haskell</option>   <option value="4">Java</option>   <option value="5">JavaScript</option>   <option value="6">Lisp</option>   <option value="7">Lua</option>   <option value="8">MATLAB</option>   <option value="9">NewLISP</option>   <option value="10">PHP</option>   <option value="11">Perl</option>   <option value="12">SQL</option>   <option value="13">Unix shell</option>   </select>   </div>      <div class="col-xs-2">   <button type="button" id="multi_d_rightAll" class="btn btn-default btn-block" style="margin-top: 20px;"><i class="glyphicon glyphicon-forward"></i></button>   <button type="button" id="multi_d_rightSelected" class="btn btn-default btn-block"><i class="glyphicon glyphicon-chevron-right"></i></button>   <button type="button" id="multi_d_leftSelected" class="btn btn-default btn-block"><i class="glyphicon glyphicon-chevron-left"></i></button>   <button type="button" id="multi_d_leftAll" class="btn btn-default btn-block"><i class="glyphicon glyphicon-backward"></i></button>      <hr style="margin: 40px 0 60px;" />      <button type="button" id="multi_d_rightAll_2" class="btn btn-default btn-block"><i class="glyphicon glyphicon-forward"></i></button>   <button type="button" id="multi_d_rightSelected_2" class="btn btn-default btn-block"><i class="glyphicon glyphicon-chevron-right"></i></button>   <button type="button" id="multi_d_leftSelected_2" class="btn btn-default btn-block"><i class="glyphicon glyphicon-chevron-left"></i></button>   <button type="button" id="multi_d_leftAll_2" class="btn btn-default btn-block"><i class="glyphicon glyphicon-backward"></i></button>   </div>      <div class="col-xs-5">   <b>Known languages</b>   <select name="to[]" id="multi_d_to" class="form-control" size="8" multiple="multiple"></select>      <br/><hr/><br/>      <b>I want to learn</b>   <select name="to_2[]" id="multi_d_to_2" class="form-control" size="8" multiple="multiple"></select>   </div>  </div>  
JavaScript
  <script type="text/javascript">  jQuery(document).ready(function($) {   $('#multi_d').multiselect({   right: '#multi_d_to, #multi_d_to_2',   rightSelected: '#multi_d_rightSelected, #multi_d_rightSelected_2',   leftSelected: '#multi_d_leftSelected, #multi_d_leftSelected_2',   rightAll: '#multi_d_rightAll, #multi_d_rightAll_2',   leftAll: '#multi_d_leftAll, #multi_d_leftAll_2',     search: {   left: '<input type="text" name="q" class="form-control" placeholder="Search..." />'   },     moveToRight: function(Multiselect, $options, event, silent, skipStack) {   var button = $(event.currentTarget).attr('id');     if (button == 'multi_d_rightSelected') {   var $left_options = Multiselect.$left.find('> option:selected');   Multiselect.$right.eq(0).append($left_options);     if ( typeof Multiselect.callbacks.sort == 'function' && !silent ) {   Multiselect.$right.eq(0).find('> option').sort(Multiselect.callbacks.sort).appendTo(Multiselect.$right.eq(0));   }   } else if (button == 'multi_d_rightAll') {   var $left_options = Multiselect.$left.children(':visible');   Multiselect.$right.eq(0).append($left_options);     if ( typeof Multiselect.callbacks.sort == 'function' && !silent ) {   Multiselect.$right.eq(0).find('> option').sort(Multiselect.callbacks.sort).appendTo(Multiselect.$right.eq(0));   }   } else if (button == 'multi_d_rightSelected_2') {   var $left_options = Multiselect.$left.find('> option:selected');   Multiselect.$right.eq(1).append($left_options);     if ( typeof Multiselect.callbacks.sort == 'function' && !silent ) {   Multiselect.$right.eq(1).find('> option').sort(Multiselect.callbacks.sort).appendTo(Multiselect.$right.eq(1));   }   } else if (button == 'multi_d_rightAll_2') {   var $left_options = Multiselect.$left.children(':visible');   Multiselect.$right.eq(1).append($left_options);     if ( typeof Multiselect.callbacks.sort == 'function' && !silent ) {   Multiselect.$right.eq(1).eq(1).find('> option').sort(Multiselect.callbacks.sort).appendTo(Multiselect.$right.eq(1));   }   }   },     moveToLeft: function(Multiselect, $options, event, silent, skipStack) {   var button = $(event.currentTarget).attr('id');     if (button == 'multi_d_leftSelected') {   var $right_options = Multiselect.$right.eq(0).find('> option:selected');   Multiselect.$left.append($right_options);     if ( typeof Multiselect.callbacks.sort == 'function' && !silent ) {   Multiselect.$left.find('> option').sort(Multiselect.callbacks.sort).appendTo(Multiselect.$left);   }   } else if (button == 'multi_d_leftAll') {   var $right_options = Multiselect.$right.eq(0).children(':visible');   Multiselect.$left.append($right_options);     if ( typeof Multiselect.callbacks.sort == 'function' && !silent ) {   Multiselect.$left.find('> option').sort(Multiselect.callbacks.sort).appendTo(Multiselect.$left);   }   } else if (button == 'multi_d_leftSelected_2') {   var $right_options = Multiselect.$right.eq(1).find('> option:selected');   Multiselect.$left.append($right_options);     if ( typeof Multiselect.callbacks.sort == 'function' && !silent ) {   Multiselect.$left.find('> option').sort(Multiselect.callbacks.sort).appendTo(Multiselect.$left);   }   } else if (button == 'multi_d_leftAll_2') {   var $right_options = Multiselect.$right.eq(1).children(':visible');   Multiselect.$left.append($right_options);     if ( typeof Multiselect.callbacks.sort == 'function' && !silent ) {   Multiselect.$left.find('> option').sort(Multiselect.callbacks.sort).appendTo(Multiselect.$left);   }   }   }   });  });  </script>  

crlcu.github.io

Выбор инструментов

Под инструментами в данном контексте я подразумеваю то, чем мы будем заменять селект. И мой выбор пал на radio кнопки по причине их схожего поведения: можно выбрать одновременно только один вариант.

 <div class="options">  <label>  <input type="radio" name="r" value="111" checked>  <div class="value">11text11</div>  </label>  ....

Мы обернули радио-кнопки в <label/>, чтобы не городить ненужных IDшников и for-ов.

Логика

Для реализации выбора нужного пункта как в селекте нам надо, чтобы он оказывался в верхней позиции списка. Для этого обозначим, что значение выбранного элемента должно позиционироваться абсолютно (вырываться из потока) и помещаться на самый верх:

#dropdown .options :checked + .value{  position: absolute;  top: 0; }

Также нам надо соблюсти правило: высота каждого пункта должна быть равной "вакантному" месту (отступу сверху) для выбранного пункта

#dropdown .options .value{  height: var(--item-height); } #dropdown .options{  padding-top: var(--item-height); }

Основная часть работает — при выборе пункта он оказывается на лидирующей позиции. Осталось озаботиться тем, чтобы в "спокойном" состоянии был виден только выбранный пункт, а остальной список раскрывался при клике, также при клике в "молоко" список должен закрываться без изменения значения.

На ум приходит манипуляция псевдоселектором :focus. Добавим какой-нибудь инпут, который будет под него попадать. Я выбрал [type=text] потому, что ему можно задать размер (растянуть на всю ширину и высоту) и заслонить им лидирующий выбранный элемент.

<div class="dropdown" id="dropdown">  <input type="text">  <div class="options">  ....

Скрывать выпадающий список будем ограничением высоты и overflow: hidden:

#dropdown .options{  padding-top: var(--item-height);  overflow: hidden;  height: 0; } #dropdown > :focus + .options{  height: var(--list-height); }

Разумеется, инпута не должно быть видно (как и радио-кнопок, представляющих опции):

#dropdown input{  opacity: 0; }

Грязный хак

И когда уже казалось, что все получилось, я столкнулся с неожиданностью: при клике по пункту меню из списка фокус с управляющего инпута уходит быстрее, чем срабатывает клик на элементе списка. То есть происходит ровно то же, чтои при клике в молоко — список просто закрывается.

Чтобы увеличить задержку до скрытия выпавшего списка, придется использовать грязный хак:

#dropdown .options{  ...  transition: 0s .1s height; }

Готово, смотрите пример (я добавил немного стилей для красоты): https://jsfiddle.net/2k1pvbyt/

Если мы пойдем дальше, то нам захочется сделать таким же образом (без JS) мультиселект. Не каждый интегратор jquery-плагинов такой сделает с JS (JQuery), а мы-то с вами ишь чего вздумали! Ну сказано — сделано, нельзя упасть лицом в грязь. Попробуем разобраться, возможно ли это. И, если нет, что в каком именно моменте нельзя обойтись без js.

Что нам нужно изменить в предыдущем примере, чтобы наш селект стал мульти? Каждый пункт должен иметь возможность быть выбранным в не зависимости от других. Очевидно, нам надо сменить радио-кнопки на чекбоксы:

 <label data-value="111">  <input type="checkbox" required>  </label>

Да, это еще не все, и required там не случайно, с его помощью мы будем манипулировать нашим списком.

 <fieldset>  <label data-value="111">  <input type="checkbox" required>  </label>  </fieldset>

Если мы обернем это в <fieldset/>, то у нас появится возможность манипулировать псевдоселекторами :valid / :invalid.

Чтобы понять, что именно мы должны сделать, надо четко сформулировать задачу:

Пусть выделенный пункт будет в начале списка. На одной строке с ним прочие выделенные пункты.

Поместить в начало списка выбранный элемент несложно, мы зададим родителю display: flex и будем играться со значением order:

#dropdown .options > fieldset:invalid{  order: 2; } #dropdown .options{  display: flex;  flex-wrap: wrap; } #dropdown fieldset{  flex-basis: 100%; }

Первое условие выполнено и, чтобы поместить все выбранные элементы на одну строку, для выбранных пунктов зададим:

#dropdown .options > fieldset:valid{  flex-basis: 10%;  z-index: 3; }

Вуаля! похоже, что работает.

Заключение

У нас не получилось выяснить, где (в рамках задачи) мы не можем обойтись без js.
Возможно (точно), на более сложных примерах так и будет.

Дублирую ссылки на примеры:

  • Селект
  • Мультиселект

Ну и для тех, кто не хочет писать эту "кучу разметки" руками, накидал скрипт, который сделает это за вас.

  • Селект
  • Мультиселект

Дополнения и прочие issue приветствуются, без сомнения!

UPD

Пришла в голову идея использовать управляющий инпут. Он пригодится, если мы отойдем от концепции nojs, будем использовать некий js-конструктор для инициализации.

Среди внесенных изменений:

  • При фокусе инпут мы не скрываем, а смещаем ниже лидирующего пункта
  • Под это дело увеличиваем «вакантное место»
  • При инициализации вешаем обработчик на этот инпут, который на событие input генерирует css-строку, необходимую для фильтрации

» Смотреть пример
» Инструкция и код

habr.com

Getting Started

The Basics

There are two ways to bind a Kendo UI MultiSelect for ASP.NET MVC:

  • server—The data is serialized to the client. No Ajax requests are made.
  • ajax—The MultiSelect makes Ajax requests to get the data.

Server Binding

Below are listed the steps for you to follow when configuring the Kendo UI MultiSelect for server binding to the Northwind Products table using Linq to SQL.

  1. Make sure you followed all the steps from the introductory article on Telerik UI for ASP.NET MVC.
  2. Create a new action method and pass the Products table as the model.

    Example
    public ActionResult Index() { NorthwindDataContext northwind = new NorthwindDataContext(); return View(northwind.Products); } 
  3. Make your view strongly typed.

     <%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<IEnumerable<MvcApplication1.Models.Product>>" %> 
     @model IEnumerable<MvcApplication1.Models.Product> 
  4. Add a server bound MultiSelect.

     <%: Html.Kendo().MultiSelect() .Name("productMultiSelect") //The name of the MultiSelect is mandatory. It specifies the "id" attribute of the widget. .DataTextField("ProductName") //Specify which property of the Product to be used by the MultiSelect as a text. .DataValueField("ProductID") //Specify which property of the Product to be used by the MultiSelect as a value. .BindTo(Model) //Pass the list of Products to the MultiSelect. %> 
     @(Html.Kendo().MultiSelect() .Name("productMultiSelect") //The name of the MultiSelect is mandatory. It specifies the "id" attribute of the widget. .DataTextField("ProductName") //Specify which property of the Product to be used by the MultiSelect as a text. .DataValueField("ProductID") //Specify which property of the Product to be used by the MultiSelect as a value. .BindTo(Model) //Pass the list of Products to the MultiSelect. ) 

Ajax Binding

Below are listed the steps for you to follow when configuring the Kendo UI MultiSelect for Ajax binding to the Northwind Products table using Linq to SQL.

  1. Make sure you followed all the steps from the introductory article on Telerik UI for ASP.NET MVC.

  2. Create an action method which renders the view.

    Example
    public ActionResult Index() { return View(); } 
  3. Create a new action method and pass the Products table as JSON result.

    Example
    public JsonResult GetProducts() { NorthwindDataContext northwind = new NorthwindDataContext(); return Json(northwind.Products, JsonRequestBehavior.AllowGet); } 
  4. Add an Ajax-bound MultiSelect.

     <%: Html.Kendo().MultiSelect() .Name("productMultiSelect") //The name of the MultiSelect is mandatory. It specifies the "id" attribute of the widget. .DataTextField("ProductName") //Specify which property of the Product to be used by the MultiSelect as a text. .DataValueField("ProductID") //Specify which property of the Product to be used by the MultiSelect as a value. .Filter(FilterType.Contains) .DataSource(source => { source.Read(read => { read.Action("GetProducts", "Home"); //Set the Action and Controller names. }) .ServerFiltering(true); //If true, the DataSource will not filter the data on the client. }) %> 
     @(Html.Kendo().MultiSelect() .Name("productMultiSelect") //The name of the MultiSelect is mandatory. It specifies the "id" attribute of the widget. .DataTextField("ProductName") //Specify which property of the Product to be used by the MultiSelect as a text. .DataValueField("ProductID") //Specify which property of the Product to be used by the MultiSelect as a value. .Filter(FilterType.Contains) .DataSource(source => { source.Read(read => { read.Action("GetProducts", "Home"); //Set the Action and Controller names. }) .ServerFiltering(true); //If true, the DataSource will not filter the data on the client. }) ) 

Important:

The ToDataSourceResult() extension method modifies the structure of the result and the widget is not able to bind to it. In this case, return a simple array of data.

ToDataSourceResult Binding

Below are listed the steps for you to follow when configuring the Kendo UI MultiSelect to use a custom DataSource and thus bind to a ToDataSourceResult instance.

  1. Make sure you followed all the steps from the introductory article on Telerik UI for ASP.NET MVC.

  2. Create an action method which renders the view.

    Example
    public ActionResult Index() { return View(); } 
  3. Create a new action method and pass the Products table as JSON result.

    Example
    public JsonResult GetProducts([DataSourceRequest] DataSourceRequest request) { NorthwindDataContext northwind = new NorthwindDataContext(); return Json(northwind.Products.ToDataSourceResult(request)); } 
  4. Add an Ajax-bound MultiSelect.

     <%: Html.Kendo().MultiSelect() .Name("productMultiSelect") .DataTextField("ProductName") //Specify which property of the Product to be used by the MultiSelect as a text. .DataValueField("ProductID") //Specify which property of the Product to be used by the MultiSelect as a value. .DataSource(source => { source.Custom() .ServerFiltering(true) .Type("aspnetmvc-ajax") //Set this type if you want to use DataSourceRequest and ToDataSourceResult instances .Transport(transport => { transport.Read("GetProducts", "Home"); }) .Schema(schema => { schema.Data("Data") //define the [data](http://docs.telerik.com/kendo-ui/api/javascript/data/datasource#configuration-schema.data) option .Total("Total"); //define the [total](http://docs.telerik.com/kendo-ui/api/javascript/data/datasource#configuration-schema.total) option }); }) %> 
     @(Html.Kendo().MultiSelect() .Name("productMultiSelect") .DataTextField("ProductName") //Specify which property of the Product to be used by the MultiSelect as a text. .DataValueField("ProductID") //Specify which property of the Product to be used by the MultiSelect as a value. .DataSource(source => { source.Custom() .ServerFiltering(true) .Type("aspnetmvc-ajax") //Set this type if you want to use DataSourceRequest and ToDataSourceResult instances. .Transport(transport => { transport.Read("GetProducts", "Home"); }) .Schema(schema => { schema.Data("Data") //define the [data](http://docs.telerik.com/kendo-ui/api/javascript/data/datasource#configuration-schema.data) option .Total("Total"); //define the [total](http://docs.telerik.com/kendo-ui/api/javascript/data/datasource#configuration-schema.total) option }); }) ) 

Model Binding

You can implement model binding both with local data and remote data, and in combination with virtualization.

Local Data

Local data is the data that is available on the client when the MultiSelect is initialized.

  1. Pass the data to the view through the view model.

    Example
    public ActionResult Index() { return View(new ProductViewModel { Orders = GetOrders(), SelectedOrders = new int[] { 1, 3 } }); } private static List<Order> GetOrders() { var orders = Enumerable.Range(0, 2000).Select(i => new Order { OrderID = i, OrderName = "OrderName" + i }); return orders.ToList(); } 
  2. Add the MultiSelect to the view and bind it to a property of the view model.

     @model MvcApplication1.Models.ProductViewModel @(Html.Kendo().MultiSelectFor(m => m.SelectedOrders) .DataValueField("OrderID") .DataTextField("OrderName") .BindTo(Model.Orders) ) 
     <%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<MvcApplication1.Models.ProductViewModel>" %> <%: Html.Kendo().MultiSelectFor(m => m.SelectedOrders) .DataValueField("OrderID") .DataTextField("OrderName") .BindTo(Model.Orders) %> 

Remote Data

You can configure the MultiSelect to get its data from a remote source by making an AJAX request.

  1. Create an action that returns the data as a JSON result.

    Example
    public ActionResult Index() { return View(new ProductViewModel { SelectedOrders = new int[] { 1, 3 } }); } public JsonResult GetOrdersAjax() { var orders = Enumerable.Range(0, 2000).Select(i => new Order { OrderID = i, OrderName = "OrderName" + i }); return Json(orders.ToList(), JsonRequestBehavior.AllowGet); } 
  2. Add the MultiSelect to the view and configure its DataSource to use remote data.

     @model MvcApplication1.Models.ProductViewModel @(Html.Kendo().MultiSelectFor(m => m.SelectedOrders) .Filter("contains") .DataValueField("OrderID") .DataTextField("OrderName") .DataSource(source => { source.Read(read => { read.Action("GetOrdersAjax", "Home"); }) .ServerFiltering(false); }) ) 
     <%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<MvcApplication1.Models.ProductViewModel>" %> <%: Html.Kendo().MultiSelectFor(m => m.SelectedOrders) .Filter("contains") .DataValueField("OrderID") .DataTextField("OrderName") .DataSource(source => { source.Read(read => { read.Action("GetOrdersAjax", "Home"); }) .ServerFiltering(false); }) %> 

Virtualization

You can configure a MultiSelect that is bound to a model field to use virtualization.

Important

If the AutoBind option of the MultiSelect is set to false, the widget will not be able to display pre-selected items until it is focused.

  1. Create the Read and ValueMapper actions.

    Example
    public ActionResult Index() { return View(new ProductViewModel { SelectedOrders = new int[] { 1, 3 } }); } [HttpPost] public ActionResult OrdersVirtualization_Read([DataSourceRequest] DataSourceRequest request) { return Json(GetOrders().ToDataSourceResult(request)); } public ActionResult Orders_ValueMapper(int[] values) { var indices = new List<int>(); if (values != null && values.Any()) { var index = 0; foreach (var order in GetOrders()) { if (values.Contains(order.OrderID)) { indices.Add(index); } index += 1; } } return Json(indices, JsonRequestBehavior.AllowGet); } private static List<Order> GetOrders() { var orders = Enumerable.Range(0, 2000).Select(i => new Order { OrderID = i, OrderName = "OrderName" + i }); return orders.ToList(); } 
  2. Add the MultiSelect to the view and configure it to use virtualization.

     @model MvcApplication1.Models.ProductViewModel @(Html.Kendo().MultiSelectFor(m => m.SelectedOrders) .Filter("contains") .DataValueField("OrderID") .DataTextField("OrderName") .DataSource(source => { source.Custom() .ServerFiltering(true) .ServerPaging(true) .PageSize(80) .Type("aspnetmvc-ajax") .Transport(transport => { transport.Read("OrdersVirtualization_Read", "Home"); }) .Schema(schema => { schema.Data("Data") .Total("Total"); }); }) .Virtual(v => v.ItemHeight(26).ValueMapper("valueMapper")) ) <script> function valueMapper(options) { $.ajax({ url: "@Url.Action("Orders_ValueMapper", "Home")", data: convertValues(options.value), success: function (data) { options.success(data); } }); } function convertValues(value) { var data = {}; value = $.isArray(value) ? value : [value]; for (var idx = 0; idx < value.length; idx++) { data["values[" + idx + "]"] = value[idx]; } return data; } </script> 
     <%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<MvcApplication1.Models.ProductViewModel>" %> <%: Html.Kendo().MultiSelectFor(m => m.SelectedOrders) .Filter("contains") .DataValueField("OrderID") .DataTextField("OrderName") .DataSource(source => { source.Custom() .ServerFiltering(true) .ServerPaging(true) .PageSize(80) .Type("aspnetmvc-ajax") .Transport(transport => { transport.Read("OrdersVirtualization_Read", "Home"); }) .Schema(schema => { schema.Data("Data") .Total("Total"); }); }) .Virtual(v => v.ItemHeight(26).ValueMapper("valueMapper")) ) <script> function valueMapper(options) { $.ajax({ url: "@Url.Action("Orders_ValueMapper", "Home")", data: convertValues(options.value), success: function (data) { options.success(data); } }); } function convertValues(value) { var data = {}; value = $.isArray(value) ? value : [value]; for (var idx = 0; idx < value.length; idx++) { data["values[" + idx + "]"] = value[idx]; } return data; } </script> %> 

Parameter Sending to Server

Below are listed the steps for you to follow when configuring the Kendo UI MultiSelect to send parameters to the server.

 <%: Html.Kendo().MultiSelect()  .Name("productMultiSelect") //The name of the MultiSelect is mandatory. It specifies the "id" attribute of the widget.  .DataTextField("ProductName") //Specify which property of the Product to be used by the MultiSelect as a text.  .DataValueField("ProductID") //Specify which property of the Product to be used by the MultiSelect as a value.  .Filter(FilterType.Contains)  .DataSource(source =>  {  source.Read(read =>  {  read.Action("GetProducts", "Home")  .Data("onAdditionalData");  });   source.serverFiltering(true);  })  %>  <script>  function onAdditionalData() {  return {  text: $("#productMultiSelect").data("kendoMultiSelect").input.val()  };  }  </script> 
 @(Html.Kendo().MultiSelect()  .Name("productMultiSelect") //The name of the MultiSelect is mandatory. It specifies the "id" attribute of the widget.  .DataTextField("ProductName") //Specify which property of the Product to be used by the MultiSelect as a text.  .DataValueField("ProductID") //Specify which property of the Product to be used by the MultiSelect as a value.  .Filter(FilterType.Contains)  .DataSource(source =>  {  source.Read(read =>  {  read.Action("GetProducts", "Home") //Set the Action and Controller names.  .Data("onAdditionalData");  });  source.ServerFiltering(true);  })  )   <script>  function onAdditionalData() {  return {  text: $("#productMultiSelect").data("kendoMultiSelect").input.val()  };  }  </script> 

The following example demonstrates how the GetProducts method is used.

Example
public JsonResult GetProducts(string text) {  var northwind = new SampleEntities();   var products = northwind.Products.Select(product => new ProductViewModel  {  ProductID = product.ProductID,  ProductName = product.ProductName,  UnitPrice = product.UnitPrice ?? 0,  UnitsInStock = product.UnitsInStock ?? 0,  UnitsOnOrder = product.UnitsOnOrder ?? 0,  Discontinued = product.Discontinued  });   if (!string.IsNullOrEmpty(text))  {  products = products.Where(p => p.ProductName.Contains(text));  }   return Json(products, JsonRequestBehavior.AllowGet); } 

Important

The Kendo UI MultiSelect has a default event handler for the Data callback of the DataSource. It is used when no event handler is defined.

The following example demonstrates how to use the default event handler for the Data callback of the DataSource.

Example
function requestData(selector) {  return { text: $(selector).data("kendoMultiSelect").input.val() }; } 

As seen, the MultiSelect sends the value of the input only if the end-user starts to type in it.

Pre-select Values on Initial Load

When deferred binding—AutoBind: false—is used, specify a list of data items instead of just a list of strings. This functionality is supported in Kendo UI Q1 SP1 2013 and later versions.

 <%= Html.Kendo().MultiSelect()  .Name("productMultiSelect") //The name of the MultiSelect is mandatory. It specifies the "id" attribute of the widget.  .DataTextField("ProductName") //Specifies which property of the Product to be used by the MultiSelect as a text.  .DataValueField("ProductID") //Specifies which property of the Product to be used by the MultiSelect as a value.  .Filter(FilterType.Contains)  .AutoBind(false)  .DataSource(source =>  {  source.Read(read =>  {  read.Action("GetProducts", "Home") //Set the Action and Controller name  .Data("onAdditionalData");  });  })  .Value(new List<Product> {  new Product { ProductName = "Chai", ProductID = 1 },  new Product { ProductName = "Chang", ProductID = 2 }  })  %> 
 @(Html.Kendo().MultiSelect()  .Name("productMultiSelect") //The name of the MultiSelect is mandatory. It specifies the "id" attribute of the widget.  .DataTextField("ProductName") //Specify which property of the Product to be used by the MultiSelect as a text.  .DataValueField("ProductID") //Specify which property of the Product to be used by the MultiSelect as a value.  .Filter(FilterType.Contains)  .AutoBind(false)  .DataSource(source =>  {  source.Read(read =>  {  read.Action("GetProducts", "Home") //Set the Action and Controller name  .Data("onAdditionalData");  });  })  .Value(new List<Product> {  new Product { ProductName = "Chai", ProductID = 1 },  new Product { ProductName = "Chang", ProductID = 2 }  })  ) 

Grouping

The MultiSelect supports binding to a grouped data source. Define a datasource group expression to group the data by using the Custom DataSource configuration.

For more information, refer to the demo on grouping.

Important

The data source sorts the grouped data either in ascending or descending order. If you want to persist a specific group order, use the server grouping feature. Use the DataSource ServerGrouping method to define the serverGrouping option.

Event Handling

You can subscribe to all MultiSelect events.

By Handler Name

The examples below demonstrates how to subscribe to events by a handler name.

 <%: Html.Kendo().MultiSelect()  .Name("multiselect")  .BindTo(new string[] { "Item1", "Item2", "Item3" })  .Events(e => e  .Select("multiselect_select")  .Change("multiselect_change")  )  %>  <script>  function multiselect_select() {  //Handle the select event.  }   function multiselect_change() {  //Handle the change event.  }  </script> 
 @(Html.Kendo().MultiSelect()  .Name("multiselect")  .BindTo(new string[] { "Item1", "Item2", "Item3" })  .Events(e => e  .Select("multiselect_select")  .Change("multiselect_change")  )  )  <script>  function multiselect_select() {  //Handle the select event.  }   function multiselect_change() {  //Handle the change event.  }  </script> 

By Template Delegate

The following example demonstrates how to subscribe to events by a template delegate.

Example
@(Html.Kendo().MultiSelect()  .Name("multiselect")  .BindTo(new string[] { "Item1", "Item2", "Item3" })  .Events(e => e  .Select(@<text>  function() {  //Handle the select event inline.  }  </text>)  .Change(@<text>  function() {  //Handle the change event inline.  }  </text>)  ) ) 

Reference

Existing Instances

To reference an existing Kendo UI MultiSelect instance, use the jQuery.data() configuration option. Once a reference is established, use the MultiSelect API to control its behavior.

Example
//Put this after your Kendo UI MultiSelect for ASP.NET MVC declaration. <script>  $(function() {  //Notice that the Name() of the MultiSelect is used to get its client-side instance.  var multiselect = $("#productMultiSelect").data("kendoMultiSelect");  }); </script> 

docs.telerik.com


You May Also Like

About the Author: admind

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Этот сайт использует Akismet для борьбы со спамом. Узнайте как обрабатываются ваши данные комментариев.