Theun Arbeider
Theun Arbeider

Reputation: 5419

How to handle a kendo multiselectfor in foreach statement

Situation

I have a list of users, which inturn has a list of roles which I want to see editable in a kendo multiselectfor.

Problem

With the code that I have it turns out very weird, at the top it display a bunch of textboxes (for my opinion) and at the place where the multiselect boxes should be all I see are up and down arrows.

My code so far:

foreach (var u in Model.Users)
{
<div class="form-group">
    <div class="col-sm-2">
        @Html.Label(u.Name)
    </div>
    <div>
        @Html.Kendo().MultiSelectFor(m => u.Roles).BindTo(Model.UserRoles);
    </div>
</div>
}

Viewmodel

public class CompanyViewModel
{
    public IEnumerable<UserDto> Users { get; set; }
    public IEnumerable<UserRoleDto> UserRoles { get; set; }
}

UserDto

public class UserDto
{
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string MiddleName { get; set; }
    public string LastName { get; set; }
    public string Name
    {
        get
        {
            return string.Format("{0} {1} {2}", FirstName, MiddleName, LastName);
        }
    }

    public IEnumerable<SelectListItem> Roles { get; set; }
}

Here's a snippet of what the page looks like when I render it like this

div class="k-widget k-multiselect k-header" unselectable="on" title style>

div class="k-multiselect-wrap k-floatrap" unselectable="on">...</div>

div class="k-widget k-multiselect k-header" unselectable="on" title style>

div class="k-multiselect-wrap k-floatrap" unselectable="on">...</div>

div class="k-widget k-multiselect k-header" unselectable="on" title style>

div class="k-multiselect-wrap k-floatrap" unselectable="on">...</div>

div class="k-widget k-multiselect k-header" unselectable="on" title style>

div class="k-multiselect-wrap k-floatrap" unselectable="on">...</div>

and this keeps repeating itself some more times.

I suspect there is an issue with how I make the multiselectfor based on the user instead of something from the model but my knowlage of kendo is to low to understand how it should be done.

Any help would be much appreciated

Upvotes: 1

Views: 1397

Answers (1)

user3559349
user3559349

Reputation:

You have multiple issues with your code.

Firstly, Kendo MultiSelectFor() creates a html <select multiple> tag (although it hides it using display: none; and adds its own html, but its the <select> that is used for binding and posting back to the controller). A <select multiple> can only bind to, and can only post back an array of simple values types, not complex objects so you cannot bind to a property which is typeof IEnumerable<IEnumerable<SelectListItem>. Your Roles property needs to be IEnumerable<int> Roles { get; set; } assuming your wanting to post back the selected role ID's.

Next your UserRoles needs to be IEnumerable<SelectListItem> or SelectList which is necessary for the .BindTo() method of the helper.

Finally you cannot use a foreach loop to bind to collection properties. It will generate duplicate name attributes that have no relationship to your model and therefor will not bind (and also duplicate id attributes which is invalid html). You need to use a custom EditorTemplate for the type in your collection (generally a for loop would be suitable, but due to an issue with using the helper in a loop, it wont work in your case)

Your view models needs to be

public class CompanyViewModel
{
  public IEnumerable<UserViewModel> Users { get; set; }
  public IEnumerable<SelectListItem> RoleList { get; set; }
}
public class UserViewModel
{
  public int Id { get; set; }
  public string FirstName { get; set; }
  ....
  [Display(Name = "Roles")]
  public IEnumerable<int> SelectedRoles { get; set; }
}

Then create a partial view /Views/Shared/EditorTemplates/UserViewModel.cshtml

@model yourAssembly.UserViewModel
@Html.HiddenFor(m => m.Id)
....
@Html.LabelFor(m => m.SelectedRoles)
@Html.Kendo().MultiSelectFor(m => u.SelectedRoles)
  .BindTo((IEnumerable<SelectListItem>)ViewData["roleList"])
....

and in the main view

@model yourAssembly.CompanyViewModel
@using (Html.BeginForm())
{
  @Html.EditorFor(m => m.Users, new { roleList = Model.RoleList })
  <input type="submit" .../>
}

What is happening here is that EditorFor() method will generate the html for each UserViewModel in the collection based on the html in the template. In addition, it passes the SelectList to the template as additional ViewData

Side note: I recommend you start by using the standard MVC listbox helper

@Html.ListBoxFor(m => u.SelectedRoles, (IEnumerable<SelectListItem>)ViewData["roleList"])

in the template to be sure its all working, and then change to @Html.Kendo().MultiSelectFor()

Upvotes: 2

Related Questions