How to use select2 with multiple options using Razor and MVC - javascript

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.

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.

Populate Dynamic DropDownListFor in Javascript

I have a couple of drop down lists... The first of which has an onchange event that calls a Javascript funcction:
#for (int i = 0; i < Model.things.Count; i++)
{
<tr>
<td>
#Html.DropDownListFor(m => m.Category[i].ID, ViewBag.Category as SelectList, "Please select a Category", new { #class = "class1", onchange = "Changed(this)" })
</td>
<td>
#Html.DropDownListFor(m => m.SubCategory[i].ID, Enumerable.Empty<SelectListItem>(), "Please select a Sub Category", new { #class = "class2" })
</td>
</tr>
}
Within this function I am making an ajax call to a controller method that returns a SelectList:
function TrackerChanged(val) {
var id = val.value;
$j.ajax({
url: appRoot + 'Controller/Method',
type: 'post',
data: { 'id': id},
success: function (results) {
if (results) {
**** POPULATE SECOND DROPDOWN ABOVE ***
}
else {
alert("Error updating comment");
}
},
failure: function () {
alert("Error updating comment");
}
});
}
The Controller Method returns a SelectList:
public SelectList Method(id categoryID)
{
IEnumerable<SelectListItem> select = null;
// Populate the IEnumerable with SubCategory results to show in the second Drop Down
return new SelectList(select, "Value", "Text");
}
but as you may notice from the comment in my ajax success chunk - I do not know how I would bind my new results back to the controller.
Please can someone help. I have looked for some examples and nothing seems to be working for me.

How to implement search with two terms for a collection?

So, currently I have a collection of items where I want the user to be able to search using the name and some random text from the collection.
Here's what I have done so far:
public IEnumerable<Item> items = new[]
{
new Item { ItemId = 1, ItemName = "Apple", ItemDescription = "crispy, sweet", ItemPairing = "Walnut"},
new Item { ItemId = 2, ItemName = "Pear", ItemDescription = "slightly tart", ItemPairing = "Cheese"},
new Item { ItemId = 3, ItemName = "Banana", ItemDescription = "good source of potassium", ItemPairing = "Honey" },
new Item { ItemId = 4, ItemName = "Chocolate", ItemDescription = "Sweet and rich melting flavor", ItemPairing = "Wine"}
};
public ActionResult Index()
{
return View("Index");
}
public ActionResult Search(string search)
{
return View("Index", items.Where(n => n.ItemName.StartsWith(search)));
}
Here's the search part on the view:
<p>
<b>Search for Name:</b>
#Html.TextBox("ItemName", "", new { #class = "form-control" })
<b>Search for Text:</b>
#Html.TextBox("ItemText", "", new { #class = "form-control" })
<input id="search" type="submit" value="Search" onclick="search(ItemName,ItemText)" />
</p>
<script>
function search(ItemName, ItemText) {
$("search").click(function () {
$.ajax({
type: "POST",
url: "/Items/Search",
data: {ItemName, ItemText},
datatype: "html",
success: function (data) {
$('#result').html(data);
}
});
});
}
</script>
So that it can look something like this:
I want it so that when the user types in Apple for Name and Crispy for text, they can find the Apple item from my collection. I also want it so that if they type in either Name or Text, it will still return a matched item.
I'm not sure how to do that.
Remove the onclick attribute from the submit button and change it to
<button type="button" id="search">Search</button>
and change the script to
var url = '#Url.Action("Search", "Items")';
$("#search").click(function () {
$.post(url, { ItemName: $('#ItemName').val(), ItemText: $('#ItemText').val() }, function(data) {
$('#result').html(data);
});
})
Note you may want to consider making it $.get() rather than $.post()
and change the controller method to accept the inputs from both textboxes
public PartialViewResult Search(string itemName, string itemText)
{
var items = ??
// filter the data (adjust to suit your needs)
if (itemName != null)
{
items = items.Where(x => x.ItemName.ToUpperInveriant().Contains(itemName.ToUpperInveriant())
}
if (itemText != null)
{
items = items.Where(x => x.ItemDescription.ToUpperInveriant().Contains(itemText.ToUpperInveriant())
}
// query you data
return PartialView("_Search", items);
}
Side note: Its not clear what the logic for search is - i.e. if you enter search text in both textboxes, do you want an and or an or search
Assuming the view in the view in the question is Index.cshtml, then it will include the following html
<div id="result">
#Html.Action("Search") // assumes you want to initially display all items
</div>
and the _Search.cshtml partial would be something like
#model IEnumerable<Item>
#foreach (var item in Model)
{
// html to display the item properties
}
While CStrouble's answer will work, if you're into AJAX calls and want to sort this in a single page, you might consider using AJAX and JQuery calls.
With AJAX & JQuery:
var search = function(itemName, itemText) {
//note that if you want RESTful link, you'll have to edit the Routing config file.
$ajax.get('/controller/action/itemName/itemText/', function(res) {
//do stuff with results... for instance:
res.forEach(function(element) {
$('#someElement').append(element);
});
});
};
And the action:
public JsonResult SearchAction(itemName, itemText) {
List<Item> newItems = items.Where(x => x.ItemName.ToUpperInveriant().Contains(itemName.ToUpperInveriant())
|| x.ItemDescription.ToUpperInveriant().Contains(itemText.ToUpperInveriant())
|| x.ItemPairing.ToUpperInveriant().Contains(itemText.ToUpperInveriant()));
return Json.Encode(newItems.ToArray());
}
Note that I'm not home, so there might be some syntax errors.
Alright, so say I want to return a partial, instead of a simple array:
first, change the action to:
public ActionResult SearchAction(itemName, itemText) {
List<Item> newItems = items.Where(x => x.ItemName.ToUpperInveriant().Contains(itemName.ToUpperInveriant())
|| x.ItemDescription.ToUpperInveriant().Contains(itemText.ToUpperInveriant())
|| x.ItemPairing.ToUpperInveriant().Contains(itemText.ToUpperInveriant()));
return PartialView("~/Views/Partials/MyPartialView.cshtml", newItems);
}
And you create a partial view in the directory you specified, taking the model that you transferred (newItems)
#model IEnumerable<Path.to.Item>
<h3>Results</h3>
<ul>
#foreach(var item in model)
{
<li>#item.Name - #item.Description</li>
}
</ul>
and now when you receive the jquery response:
$.ajax({
type: "POST",
url: "/Items/Search",
data: {ItemName, ItemText},
datatype: "html",
success: function (data) {
$('#result').html(data);
}
});
//since data is the HTML partial itself.
//if you have firebug for Mozilla firefox, you can see what I'm
//talking about
You may want to consider converting both strings to upper or lower, depending on your search requirements. The below code assumes the search is case-insensitive.
public ActionResult Search(string search)
{
string upperSearch = search.ToUpperInvariant();
return View("Index", items.Where(n =>
n.ItemName.ToUpperInvariant().StartsWith(search) ||
n.ItemDescription.ToUpperInvariant().Contains(search)));
}
with the Or condition it will match if either the text or the name matches. If they match different items (like Apple and Tart), you may need to consider what happens in that use-case, but in this case it will return both Apple and Pear.

Get selected value of Kendo grid on another View

So yesterday I started learning javascript and web development for a project at work. We are using a MVC pattern and I am having issues figuring out exactly how the javascript classes work with the views. Any help on this will be appreciated. Like I said, my knowledge is very limited. I do, however, know C# and WPF (MVVM) so maybe some of that knowledge will help me here.
We use Kendo controls. Some of the javascript for our kendo grid is below.
grid.js:
function onChange(e) {
//get currently selected dataItem
var grid = e.sender;
var selectedRow = grid.select();
var dataItem = grid.dataItem(selectedRow);
var y = $.ajax({
url: "/api/ServiceOrderData/" + dataItem.id,
type: 'GET',
dataType: 'json'
});
}
$("#serviceOrderList").kendoGrid({
groupable: true,
scrollable: true,
change: onChange,
sortable: true,
selectable: "row",
resizable: true,
pageable: true,
height: 420,
columns: [
{ field: 'PriorityCodeName', title: ' ', width: 50 },
{ field: 'ServiceOrderNumber', title: 'SO#' },
{ field: 'ServiceOrderTypeName', title: 'Type' },
{ field: 'ScheduledDate', title: 'Scheduled Date' },
{ field: 'StreetNumber', title: 'ST#', width: '11%' },
{ field: 'StreetName', title: 'Street Name' },
{ field: 'City', title: 'City' },
{ field: 'State', title: 'ST.' },
{ field: 'IsClaimed', title: 'Claimed'}
],
dataSource: serviceOrderListDataSource
});
I am wanting to be able to use the value from the onChange function:
var dataItem = grid.dataItem(selectedRow);
in the following view.
ESRIMapView.cshtml:
<body class="claro">
<div id="mainWindow" data-dojo-type="dijit/layout/BorderContainer"
data-dojo-props="design:'sidebar', gutters:false"
style="width:100%; height:100%;">
<div id="leftPane"
data-dojo-type="dijit/layout/ContentPane"
data-dojo-props="region:'left'">
<br>
<textarea type="text" id="address" />*THIS IS WHERE I WILL USE dataItem! dataItem.StreetNumber (syntax?) to be exact</textArea>
<br>
<button id="locate" data-dojo-type="dijit/form/Button">Locate</button>
</div>
<div id="map"
data-dojo-type="dijit/layout/ContentPane"
data-dojo-props="region:'center'">
</div>
</div>
</body>
Right now my ESRIMapView is loaded when the user clicks on a button on the index.cshtml screen which contains the grid that I am trying to get the value from.
<li>#Html.ActionLink("Map", "ESRIMapView", "Home", null, new { #class = "k-button" })</li>
This is my "Home" controller:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Http;
using System.Web.Mvc;
using System.Web.Services.Description;
using Alliance.MFS.Data.Client.Models;
using Alliance.MFS.Data.Local.Repositories;
namespace AllianceMobileWeb.Controllers
{
public class HomeController : Controller
{
public ActionResult Index()
{
ViewBag.Message = "Welcome to ASP.NET MVC!";
return View();
}
public ActionResult About()
{
ViewBag.Message = "Your app description page.";
return View();
}
public ActionResult ServiceOrderMaintenance()
{
ViewBag.Message = "Your contact page.";
return View();
}
public ActionResult ESRIMapView()
{
ViewBag.Message = "Your contact page.";
return View();
}
}
}
I realize this is probably a very elementary question, but any help would be appreciate. And please be as detailed as possible with your responses :)
Since you create your link before returning the (initial) view to the user, you need a bit of trickery to change it. I recommend the following: set an id on your a element and change its href attribute; on your controller, set a parameter corresponding to the street number and pre-fill the view:
Controller:
public ActionResult ESRIMapView(string streetNumber)
{
ViewBag.Message = "Your contact page.";
ViewBag.StreetNumber = streetNumber;
return View();
}
View containing the li (note the Id on the a element):
<li>#Html.ActionLink("Map", "ESRIMapView", "Home", null, new { #class = "k-button", id="myMapaction" })</li>
View containing the textarea (ESRIMapView ):
<textarea type="text" id="address" />#ViewBag.StreetNumber</textArea>
grid.js:
function onChange(e) {
//get currently selected dataItem
var grid = e.sender;
var selectedRow = grid.select();
var dataItem = grid.dataItem(selectedRow);
//change the link
var actionElem = $("#myMapaction");
var url = actionElem.attr("href");
if (url.indexOf("=") === -1) { //first time selecting a row
url += "?streetNumber=" + dataItem.StreetNumber;
} else {
url = url.substring(0, url.lastIndexOf("=") +1) + dataItem.StreetNumber;
}
actionElem.attr("href", url);
//change the link
var y = $.ajax({
url: "/api/ServiceOrderData/" + dataItem.id,
type: 'GET',
dataType: 'json'
});
}
This script simply adds the street number parameter in the query string. When the user selects a row for the first time, the streetNumber parameter is not present in the query string. After the first time, the parameter is there and we must change only the value.
Please note that this solution has its limitations: it does not work if you have other parameters in the query string (the logic for adding/editing the parameter must be changed).

In jquery autocomplete, how to disply label and submit the id corresponding to label

I have a search field which use jquery autocomplete.In this textbox that drops down a list of employee names suggested by the autocomplete.But when my form is submitted I don't want the persons name sent along with form, I want the employee id sent with the form.How I can do that?
<input id="employee">
<input type="hidden" id="employee_id">
Above given is the textfield I used
$(function () {
$.ajax({
url: '/accounts/allEmp',
type: "get",
cache: false,
success: function (data) {
var arr = [];
arr = data.employee;
$("#employee").autocomplete({
minLength: 0,
source: arr,
focus: function (event, ui) {
$("#employee").val(ui.item.name);
return false;
},
select: function (event, ui) {
$("#employee").val(ui.item.name);
$("#employee_id").val(ui.item.id);
return false;
}
})
.data("ui-autocomplete")._renderItem = function (ul, item) {
return $("<li>")
.append("<a>" + item.name)
.appendTo(ul);
};
}
});
});
This is the script I used.While I entering characters in the textfield it doesn't show any results but when remove those characters from textfield it will show all the entities(sorting is not working).And the array look like
array="employee":
[ { "name": "a", "id": 1 },
{ "name": "b", "id": 2 },
{ "name": "c", "id": 3 } ]
Please help me.Thanks in advance.
Do you control server side of source url? If so I suggest you use "value" property name instead of "name" as described in autocomplete api.
Otherwise you can modify that array in place and add "value" properties on the fly.
Here is fiddle I just created from your snippet. With click on "send" it crashes I don't know why but if you look in your developer's panel network request is getting through with params:
employee[name]:Fred
employee[id]:first
DEMO
JS code:
var data = {
json: "{\"employee\":[{\"value\":\"A\",\"id\":\"1\"},{\"value\":\"B\",\"id\":\"2\"},{\"value\":\"C\",\"id\":\"3\"}]}"
}
$(function () {
$('#submit').click(function(){
alert('Employee name = '+$('#employee_name').val()+' Employee id = '+$('#employee_id').val());
});
$.ajax({
url:"/echo/json/",
data: data,
type: "POST",
success:function(data) {
console.log(data);
var arr = data.employee;
$("#employee_name").autocomplete({
minLength: 0,
source: arr,
select: function (event, ui) {
$("#employee_name").val(ui.item.value);
$("#employee_id").val(ui.item.id);
return false;
}
});
}
});
});
HTML code:
<form>
Employee name (Type like "A" or "B" or "C")<br>
<input id="employee_name" name="employee_name">
<br>
Selected Employee id (hidden field):
<input type="text" id="employee_id" name="employee_id" readonly>
<br>
<input type="button" value="Submit" name="submit" id="submit" onclick="check_fields()">

Categories

Resources