0
votes

I've gotten the multiselect to work inside the grid for existing items but when I select "Add New Record" I get a ReferenceError: Roles is not defined in the javascript.

I'm trying to populate it with a list of IdentityRoles (the build in EF ones).

Controller: public class ManageAccountController : Controller { private ApplicationDbContext db = new ApplicationDbContext();

    public ActionResult Index()
    {
        List<RoleViewModel> roles = new List<RoleViewModel>();

        foreach(var _role in db.Roles)
        {
            roles.Add(new RoleViewModel { Id = _role.Id, Name = _role.Name });
        }


        ViewData["roles"] = roles;
        ViewData["defaultRoles"] = roles;
        return View();
    }

    public ActionResult ApplicationUsers_Read([DataSourceRequest]DataSourceRequest request)
    {

        var roleManager = new ApplicationRoleManager(new RoleStore<IdentityRole>(db));


        IQueryable<CustomUser> applicationusers;

        applicationusers = (from u in db.Users
                 select new CustomUser
                 {
                     Email = u.Email,
                     UserName = u.UserName,
                     PhoneNumber = u.PhoneNumber,
                     AccessFailedCount = u.AccessFailedCount,
                     LockoutEnabled = u.LockoutEnabled,
                     Password = "New",
                     Roles = (ICollection<IdentityRole>)db.Roles.Where(x => x.Users.Select(r => r.UserId).Contains(u.Id))
                 });



        DataSourceResult result = applicationusers.ToDataSourceResult(request, applicationUser => new {
            Email = applicationUser.Email,
            PhoneNumber = applicationUser.PhoneNumber,
            LockoutEnabled = applicationUser.LockoutEnabled,
            AccessFailedCount = applicationUser.AccessFailedCount,
            UserName = applicationUser.UserName,
            Password = applicationUser.Password,
            Roles = applicationUser.Roles
        });

        return Json(result);
    }

    //other non interesting stuff
}

Razor view:

@(Html.Kendo().Grid<GrindrodDataCapture.Models.CustomUser>
                ()
                .Name("grid")
                .Columns(columns =>
                {
                    columns.Bound(c => c.Email).ClientTemplate("<div class=\"forTooltips\">#=Email#</div>").HtmlAttributes(new { @class = "overridePadding" });
                    columns.Bound(c => c.PhoneNumber);
                    columns.Bound(c => c.LockoutEnabled).Width(110).ClientTemplate("<input type='Checkbox'  disabled />");
                    columns.Bound(c => c.AccessFailedCount).Filterable(false).Sortable(false);
                    columns.Bound(c => c.UserName);
                    columns.Bound(c => c.Password).ClientTemplate("<input type='Password' style='width: 90px' disabled />").Filterable(false).Width(120);
                    columns.Bound(p => p.Roles).ClientTemplate("#=rolesTemplate(Roles)#").Width(200);/*.ClientTemplate("#=Roles.Name#").Width(120);*/
                    columns.Command(command => { command.Edit(); command.Destroy(); }).Width(200);
                })
                .ToolBar(toolbar =>
                {
                    toolbar.Create();
                })
                .Editable(editable => editable.Mode(GridEditMode.InLine))
                .Pageable()
                .Sortable(sortable =>
                {
                    sortable.SortMode(GridSortMode.SingleColumn);
                })
                .Filterable()
                .Scrollable()
                .DataSource(dataSource => dataSource
                .Ajax()
                .Model(model =>
                {
                    model.Id(p => p.UserName);
                    model.Field(p => p.AccessFailedCount).Editable(false);
                    model.Field(p => p.Roles).DefaultValue(ViewData["roles"] as Microsoft.AspNet.Identity.EntityFramework.IdentityRole);
                })
                .Read(read => read.Action("ApplicationUsers_Read", "ManageAccount"))
                .Create(create => create.Action("ApplicationUsers_Create", "ManageAccount"))
                .Update(update => update.Action("ApplicationUsers_Update", "ManageAccount"))
                .Destroy(destroy => destroy.Action("ApplicationUsers_Destroy", "ManageAccount"))
               )
)

@(Html.Kendo().Tooltip()
              .For("#grid")
              .Filter(".forTooltips")
              .ContentHandler("getEmailTooltip")
              .Position(TooltipPosition.Right)
              .AutoHide(true)
)
 <script type="text/kendo" id="rolesTemplate">
<ul>
    #if(data){#
    #for(var i = 0; i< data.length; i++){#
    <li>#:data[i].Name#</li>
    #}#
    #}#
</ul>
</script>
<script type="text/javascript">
var rolesTemplate = kendo.template($("#rolesTemplate").html(), { useWithBlock: false });
</script>
<script>
function getEmailTooltip(e) {
    var dataItem = $("#grid").data("kendoGrid").dataItem(e.target.closest("tr"));
    var content = dataItem.Email;
    return content;
}

</script>

Model:

    public class CustomUser 
{
    [Required]
    [DataType(DataType.Password)]
    [Display(Name = "Password")]
    public string Password { get; set; }

    [Required]
    [Display(Name = "Email")]
    public string Email { get; set; }

    [Phone]
    [Display(Name = "Phone Number")]
    public string PhoneNumber { get; set; }

    [Required]
    [Display(Name = "User Name")]
    public string UserName { get; set; }

    [Required]
    [Display(Name = "Failed Logins")]
    public int AccessFailedCount { get; set; }

    [Required]
    [Display(Name = "Locked?")]
    public bool LockoutEnabled { get; set; }


    [UIHint("ClientRoles")]
    [Display(Name = "Roles")]
    public ICollection<IdentityRole> Roles { get; set; }

}

public class RoleViewModel
{

    public string Id { get; set; }

    public string Name { get; set; }
}

I've got an EditorTemplate saved in the right place caled ClientRoles:

@model Microsoft.AspNet.Identity.EntityFramework.IdentityRole
@(Html.Kendo().MultiSelect()
    .Name("Roles") //The name of the MultiSelect is mandatory. It specifies the "id" attribute of the widget.
    .DataTextField("Name") //Specify which property of the Roles to be used by the MultiSelect as a text.
    .DataValueField("Id") //Specify which property of the Roles to be used by the MultiSelect as a value.
    .BindTo((System.Collections.ICollection)ViewData["roles"]) //Pass the list of Roles to the MultiSelect.
)
2

2 Answers

1
votes

Not too sure but your Roles seem to be incorrectly cast across your implementation. ViewData["roles"] is a List<RoleViewModel>.

In your editor template change

.BindTo((System.Collections.ICollection)ViewData["roles"])

to something like:

.BindTo((Models.RoleViewModel)ViewData["roles"])

Also

.Model(model => {
    model.Id(p => p.UserName);
    model.Field(p => p.AccessFailedCount).Editable(false);
    model.Field(p => p.Roles).DefaultValue(ViewData["roles"] as Microsoft.AspNet.Identity.EntityFramework.
}

should possibly be of type List<RoleViewModel>.

I see that your CustomUser model has a public ICollection<IdentityRole> Roles { get; set; } property, so it could get a bit confusing.

0
votes

I finally figured it out! One creating a row the multi select data is null and Telerik doesn’t create the parameter on creating a new row.

I had to change the following:

columns.Bound(c => c.Roles).ClientTemplate("#=rolesTemplate(Roles)#").Width(200).Filterable(false);

To:

columns.Bound(c => c.Roles).ClientTemplate("#=(data.Roles) ? rolesTemplate(Roles) : ''#").Width(200).Filterable(false);