Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Fill Select2 dropdown box from database in MVC 4

I need help writing the jquery/ajax to fill a Select2 dropdown box.

For those who don't know what Select2 is, it is a javascript extension to provide Twitter Bootstrap looks and search / type-ahead functionality to an html select list dropdown box. For more information look at the examples here: Select2 Github page


UPDATED - Solved!

So I finally put this all together, and the solution to my problems was that I was missing functions to format the results and the list selection. The code below produces a functioning Select2 dropbox with type-ahead perfectly.

Json Method on Controller:

public JsonResult FetchItems(string query)
{
    DatabaseContext dbContext = new DatabaseContext(); //init dbContext
    List<Item> itemsList = dbContext.Items.ToList(); //fetch list of items from db table
    List<Item> resultsList = new List<Item>; //create empty results list
    foreach(var item in itemsList)
    {   
        //if any item contains the query string
        if (item.ItemName.IndexOf(query, StringComparison.OrdinalIgnoreCase) >= 0) 
        {
            resultsList.Add(item); //then add item to the results list
        }
    }
    resultsList.Sort(delegate(Item c1, Item c2) { return c1.ItemName.CompareTo(c2.ItemName); }); //sort the results list alphabetically by ItemName
    var serialisedJson = from result in resultsList //serialise the results list into json
        select new
        {
            name = result.ItemName, //each json object will have 
            id = result.ItemID      //these two variables [name, id]
        };
    return Json(serialisedJson , JsonRequestBehavior.AllowGet); //return the serialised results list
}

The Json controller method above returns a list of serialised Json objects, whose ItemName contains the string 'query' provided (this 'query' comes from the search box in the Select2 drop box).

The code below is the Javascript in the view(or layout if you prefer) to power the Select2 drop box.

Javascript:

$("#hiddenHtmlInput").select2({
    initSelection: function (element, callback) {
        var elementText = "@ViewBag.currentItemName";
        callback({ "name": elementText });
    },
    placeholder: "Select an Item",
    allowClear: true,
    style: "display: inline-block",
    minimumInputLength: 2, //you can specify a min. query length to return results
    ajax:{
        cache: false,
        dataType: "json",
        type: "GET",
        url: "@Url.Action("JsonControllerMethod", "ControllerName")",
        data: function (searchTerm) {
            return { query: searchTerm };
        },
        results: function (data) { 
            return {results: data}; 
        }
    },
    formatResult: itemFormatResult,
    formatSelection: function(item){
        return item.name;
    }
    escapeMarkup: function (m) { return m; }
});

Then in the body of the view you need a hidden Input element, which Select2 will render the dropbox to.

Html:

<input id="hiddenHtmlInput" type="hidden" class="bigdrop" style="width: 30%" value=""/>

Or attach a MVC Razor html.hidden element to your view model to enable you to post the picked item Id back to the server.

Html (MVC Razor):

@Html.HiddenFor(m => m.ItemModel.ItemId, new { id = "hiddenHtmlInput", @class = "bigdrop", style = "width: 30%", placeholder = "Select an Item" })
like image 887
Whiplash450 Avatar asked Feb 13 '13 12:02

Whiplash450


3 Answers

Solved! Finally.

The full jquery is below, what was needed were two functions to format the returned results from the controller. This is because the dropbox needs some html markup to be wrapped around the results in order to be able to display them.

Also contractID was needed as an attribute in the controller as without it results were shown in the dropdown, but they could not be selected.

$("#contractName").select2({
    placeholder: "Type to find a Contract",
    allowClear: true,
    minimumInputLength: 2,
    ajax: {
        cache: false,
        dataType: "json",
        type: "GET",
        url: "@Url.Action("FetchContracts", "Leads")",
        data: function(searchTerm){
            return { query: searchTerm };
        },
        results: function(data){
            return { results: data };
        }
    },
    formatResult: contractFormatResult,
    formatSelection: contractFormatSelection,
    escapeMarkup: function (m) { return m; }
});


function contractFormatResult(contract) {
    var markup = "<table class='contract-result'><tr>";
    if (contract.name !== undefined) {
        markup += "<div class='contract-name'>" + contract.name + "</div>";
    }
    markup += "</td></tr></table>"
    return markup;
}

function contractFormatSelection(contract) {
    return contract.name;
}
like image 95
Whiplash450 Avatar answered Dec 08 '22 13:12

Whiplash450


The problem is that you are returning a List<Contract> from that controller method, but the MVC runtime doesn't know how to hand that off to the browser. You need to return a JsonResult instead:

public JsonResult FetchContracts() 
{
    TelemarketingContext teleContext = new TelemarketingContext();
    var contracts = teleContext.Contracts.ToList();
    var json = from contract in contracts 
        select new {
            name = contract.ContractName,
            id = contract.ContactID,
        };
    return Json(json, JsonRequestBehavior.AllowGet);
}

Now, the data param of the AJAX : Success function will be the JSON from the controller. I'm not familiar with how this plugin works, but you should be able to loop through the json in data manually if you need to.

like image 32
Graham Avatar answered Dec 08 '22 12:12

Graham


Select 2 seems to be a standard select with jquery attached so this should work:

Model:

  public class vmDropDown
  {
    public IEnumerable<SelectListItem> DeviceList { get; set; }
    [Required(ErrorMessage = "Please select at least one item")]
    public IEnumerable<int> SelectedItems { get; set; }
  }

Controller:

 [HttpGet]
    public ActionResult Assign(int id)
    {
      return View(CreateUnassignedModel(id));
    }

[HttpPost]
public ActionResult Assign(vmDeviceAssign model)
{
  if (ModelState.IsValid)
  {
    _deviceLogic.Assign(model.GroupId, model.SelectedItems);
    return View("ConfirmDevice");
  }
  else // Validation error, so redisplay data entry form
  {
    return View(CreateUnassignedModel(model.GroupId));
  }
}

private vmDeviceAssign CreateUnassignedModel(int id)
{
  return new vmDeviceAssign
  {
    DeviceList = _deviceLogic.GetUnassigned(),
    SelectedItems = null
  };
}

View:

<div class="editor-field">
    @Html.ListBoxFor(model => model.SelectedItems, new SelectList(Model.DeviceList, "Value", "Text"))
    @Html.ValidationMessageFor(model => model.SelectedItems)
</div>

Cant give explanation as am at work but if you leave a message ill pick it up tonight

like image 40
DavidB Avatar answered Dec 08 '22 12:12

DavidB