Fill Select2 dropdown box from database in MVC 4
Asked Answered
C

3

9

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" })
Clymer answered 13/2, 2013 at 12:31 Comment(2)
I just did this yesterday, although to a normal dropdown. You use SelectListItem. I can give code but what is "Select2"?Coelenteron
Select2 is an additional plugin which gives dropdown boxes with v. nice functionality in a Bootstrap style. ivaynberg.github.com/select2Clymer
C
3

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;
}
Clymer answered 14/2, 2013 at 14:0 Comment(1)
Glad you got it working using formatResult. Just as an FYI (and in case anyone else runs into this problem) part of the issue was that by default select2 expects items keys named text and id in the json response. You were returning name and id which is why select2 wasn't rendering the results automatically. Something like this in your controller probably would have worked: select new { text = contract.ContractName, id = contract.ContractID };Vallee
C
1

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.

Chalcedony answered 13/2, 2013 at 13:35 Comment(1)
Ok thanks. This gives me something to go on. I'll set it up and see what I get.Clymer
C
1

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

Coelenteron answered 13/2, 2013 at 14:55 Comment(2)
I see how this could work as a work-around, but I need to use the functionality that Select2 (e.g. searching in the dropbox)Clymer
Yes sorry, I was answering a more generic question about populating ddl's. I think Grahams answer is worth exploring. Good luck!Coelenteron

© 2022 - 2024 — McMap. All rights reserved.