This project is read-only.

Lookup

lookupinput.png
it makes a readonly textbox with a button near it, on button click a window pops up, where one or multiple values can be selected

single select

select by double clicking on a row or click the row once and click ok
lookuppop.png

multiple select

select by dragging and dropping rows from top to bottom and backwards, also by clicking the + and ^ buttons, click ok at the end
lookupmultipop.png

the lookup also has search and paging with a "more results" button. by default there's a single textbox for the search, but this can be changed/overriden.

you can call it in many ways:
@Html.Lookup("Country")
@Html.LookupFor(o => o.Character, controller: "PersonLookup", multiselect: true)

It requires a controller which by default should have the name property name + "LookupController", or specify the controller name in helper's parameters or using LookupAttribute (when using EditorFor)
[Lookup(Controller = "HobbyLookup)]
public int? HobbyId { get; set; }

This controller must inherit Omu.Awesome.Mvc.LookupController, depending whether you are going to use this Lookup with single or multi select this controller can look different.

single-select requires

  • Search action - used to display available items to select from
  • Get action - used to show text in the readonly textbox (selected item)
  • item.ascx/cshtml view (Views\FruitLookup\item.ascx)
  • in the Search action the Awesome\LookupList view must be returned
    public class FruitLookupController : LookupController
    {
        public ActionResult Search(string search)
        {
            return View(@"Awesome\LookupList", fruitRepo.GetAll().Where(o => o.Name.StarsWith(search)));
        }

        public ActionResult Get(int id)
        {
            return Content(repo.Get(id).Name);
        }
    }

the item.ascx must have an <li with a data-value attribute which will contain the id of the item (or any other property that you want to use as a key)
<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<Fruit>" %>
<li data-value='<%=Model.Id %>'>
<%=Model.Name %>
</li>

done, now you can use it like this:
<%=Html.Lookup("Fruit") %>

multiselect requires

  • Search action - used to display available items to select from
  • Selected action - used to display the selected items in the lookup window
  • GetMultiple action - used to display selected items in the control
  • item.ascx/cshtml view (Views\FruitLookup\item.ascx)
  • in the Search action the Awesome\LookupList view must be returned

the Search Action will receive an IEnumerable selected which will contain the already selected items , this is done to be able to exclude the selected items from the search result

    public class FruitsLookupController : LookupController
    {  
         //available items (displayed at the top)   
        public ActionResult Search(string search, IEnumerable<int> selected)
        {
            var result = fruitRepo.Where(o => o.Name.StartsWith(search));
            
            //exclude already selected items from the search result
            if (selected != null) result = result.Where(o => !selected.Contains(o.Id));

            return View(@"Awesome\LookupList", result);
        }

        //selected items (displayed ad the bottom)
        public ActionResult Selected(IEnumerable<int> selected)
        {
            var result = selected != null ? repo.GetAll().Where(o => selected.Contains(o.Id)) : null;
            return View(@"Awesome\LookupList", result);
        }
        
        //display the selected items in the control
        public ActionResult GetMultiple(IEnumerable<int> selected)
        {
            return Json(fruitRepo.Where(o => selected.Contains(o.Id)).Select(o => new { Text = o.Name }));
        }
    }

the item.ascx for multiselect, exactly the same rules as with single-select
except, if you want to have the + and ^ buttons then you must add these css classes and the <span that you see below:
<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<Fruit>" %>
<li class="ae-lookup-item" data-value='<%=Model.Id %>'>
<span class="ae-lookup-mbtn">
</span>
    <%=Model.Name %>
</li>

now you can use it like this:
<%=Html.Lookup("Fruits", multiselect:true) %>

or with the EditorFor use the attribute
 [UIHint("Lookup")]        
 [Lookup(Multiselect = true)]
 public IEnumerable<int> Fruits { get; set; }

Paging

when using paging: true the Search Action (multi or single select) will get a parameter page, and it must return a Json { rows, more }
  • rows will contain the rendered html of the search result
  • more is a boolean that tells if there are more results
RenderView controller extension from Omu.Awesome.Mvc can be used to get the html for rows

public class FruitLookupController : LookupController
{
        ...
        public ActionResult Search(..., int? page)
        {
           //obtain the result somehow (an IEnumerable<Fruit>)
            ...            
            const int pageSize = 7;
            var rows = this.RenderView(@"Awesome\LookupList", result.Skip((page.Value - 1) * pageSize).Take(pageSize));
            return Json(new { rows, more = result.Count() > page * pageSize });
        }
}

usage:
<%=Htmll.Lookup("Fruit", paging:true) %>
// or attribute [Lookup(..., Paging = true)]

Custom search

by default you will get a single textbox for searching (it's from Awesome/SearchForm.ascx)
to change this you must override the SearchForm Action and return your own view for searching

like you could do this view (PersonLookup/SearchForm.cshtml):
First Name: <input type="text" name="fname" />
Last Name: <input type="text" name="lname" />
Country: @Html.AjaxDropdown("scountry", controller:"CountryAjaxDropdown")
<input type="submit" value="search" />

and do this in your controller (PersonLookupController):
public class PersonLookupController : LookupController
{
    public override ActionResult SearchForm()
    {
       return View();
    }

    public ActionResult Search(string fname, string lname, int? scountry)
    {
       ...
    }
}

Misc

  • just like with the AjaxDropdown you can send additional "data" to the lookup, except here the value of the lookup is not going to change when some editor from data will change it's value
  • parameters with predefined values can be sent (exactly like with the AjaxDropdown)
  • besides SearchForm() action there is a Header action which also can be ovverriden to return a custom view that will be rendered above the search results

More Examples

single-select

example of a lookup controller with just single select:
    public class CountryIdLookupController : LookupController
    {
      ...
        [HttpPost]
        public ActionResult Search(string search)
        {
            return View(@"Awesome\LookupList", repo.GetAll().Where(o => o.Name.Contains(search)));
        }

        //returns the text that is going to be set into the textbox near the lookup button 
        public ActionResult Get(int id)
        {      
            return Content((repo.Get(id) ?? new Country()).Name);
        }
    }

multi-select

example of lookup controller with multi-select support:
    public class FruitLookupController : LookupController
    {
        public IEnumerable<Fruit> data = new[]
                                             {
                                                 new Fruit{Id = 1, Name = "apple"},
                                                 new Fruit{Id = 2, Name = "pineapple"},
                                                 new Fruit{Id = 3, Name = "orange"},
                                                 new Fruit{Id = 4, Name = "banana"},
                                                 new Fruit{Id = 5, Name = "mango"},
                                             };

        [HttpPost]
        public ActionResult Search(string search, IEnumerable<int> selected)
        {
            var result = data.Where(o => o.Name.StartsWith(search));
            if(selected != null) result = result.Where(o => !selected.Contains(o.Id));

            return View(@"Awesome\LookupList", result);
        }

        public ActionResult Selected(IEnumerable<int> selected)
        {
            var result = selected != null ? data.Where(o => selected.Contains(o.Id)) : null;
            return View(@"Awesome\LookupList", result);
        }

        public ActionResult GetMultiple(IEnumerable<int> selected)
        {
            return Json(data.Where(o => selected.Contains(o.Id)).Select(o => new{Text = o.Name}));
        }
    }

    public class Fruit
    {
        public int Id { get; set; }
        public string Name { get; set; }
    }

Views/FruitLookup/item.cshtml for this:
@model Omu.AwesomeDemo.WebUI.Controllers.Fruit
<li class="ae-lookup-item" data-value="@Model.Id">
<span class="ae-lookup-mbtn" style="float:left;">
</span>
    @Model.Name
</li>

example of lookup controller with support for single-select, multi-select, paging = false and paging = true
    public class HobbyLookupController : LookupController
    {
...
        //selected is the selected values when multiselect = true
        [HttpPost]
        public ActionResult Search(string search, IEnumerable<int> selected, int? page = null)
        {
            if (page.HasValue)
            {
                const int pageSize = 6;
                var src =
                    repo.GetAll().Where(o => (selected == null || !selected.Contains(o.Id)) && o.Name.Containz(search));
                var rows = this.RenderView(@"Awesome\LookupList", src.Skip((page.Value - 1) * pageSize).Take(pageSize));
                return Json(new { rows, more = src.Count() > page * pageSize });
            }
            return View(@"Awesome\LookupList", repo.GetAll().Where(o => (selected == null || !selected.Contains(o.Id)) && o.Name.Containz(search)));
        }

        //gets the text do display in the textbox when multiselect = false
        public ActionResult Get(int id)
        {
            return Content((repo.Get(id) ?? new Hobby()).Name);
        }

        #region needed only if you use multiselect = true
        public ActionResult Selected(IEnumerable<int> selected)
        {
            return View(@"Awesome\LookupList", repo.GetAll().Where(o => selected != null && selected.Contains(o.Id)));
        }

        public ActionResult GetMultiple(IEnumerable<int> selected)
        {
            return Json(repo.GetAll().Where(o => selected.Contains(o.Id)).Select(v => new { Text = v.Name }));
        }
        #endregion
    }

Last edited May 12, 2011 at 1:38 PM by o, version 9

Comments

RealKenny Oct 22, 2013 at 6:50 PM 
Hello o, is there a way to make certain items non-selectable? (for PIGD/ICMS :) )

o Oct 22, 2011 at 8:12 PM 
@fdomendezcr in C# you could use @get but in VB don't know try some microsoft forums or stackoverflow

fdomendezcr Oct 7, 2011 at 6:59 PM 
Hello,

I try to use the lookup helper in a project using MVC 3 with Visual Basic code. In the lookupController i can't create a the method 'Get' beacuase is a reserve word. What another solution may i have?