1

我正在尝试实现一个权限屏幕,其中用户可以在特定屏幕上获得特定权限。为此,我正在生成一个 Checkboxfor 集合,绑定到一个 bool 属性集合。但是当我提交表单时,我要么得到所有 bool 属性 true 要么全部 false,这取决于我在 viewmodel 构造函数中将这些属性初始化为 true 还是 false。

这是 ViewModel 的代码:

方法一:

public class MyViewModel
{
    public MyModel Model { get; set; }        

    public IEnumerable<ScreenType> Screens { get; set; }
    public IEnumerable<SecurityType> SecurityTypes { get; set; }
    public List<PermissionType> Permissions { get; set; }        

    public MyViewModel()
    {
        LoadScreens();
        LoadSecurityTypes();
        LoadPermissions();
    }

    public void LoadPermissions()
    {
        Permissions = new List<PermissionType>();

        foreach (var screen in Screens)
        {
            foreach (var securityType in SecurityTypes)
            {
                Permissions.Add(
                    new PermissionType
                    {
                        PermissionId= Guid.NewGuid(),
                        ScreenId= screen.Id,
                        SecurityId = securityType.Id,
                        IsAllowed = false
                    });
            }
        }
    }    
}

方法二

public class MyViewModel
{
    public MyModel Model { get; set; }        

    public IEnumerable<ScreenType> Screens { get; set; }
    public IEnumerable<SecurityType> SecurityTypes { get; set; }
    public List<bool> AllowedList { get; set; }        

    public MyViewModel()
    {
        LoadScreens();
        LoadSecurityTypes();
        LoadPermissions();
    }

    public void LoadPermissions()
    {
        AllowedList = new List<bool>();

        foreach (var form in Forms)
        {
            foreach (var security in Securities)
            {
                AllowedList.Add(false);
            }
        }
    }    
}

这是我的观点的代码:

方法一:

    @using (Ajax.BeginForm("Create", "Role", null, new AjaxOptions { UpdateTargetId = "addStatus", InsertionMode = InsertionMode.Replace, OnSuccess = "onFormPostSuccess" }, new { @id = "AddForm" }))
    {  
        <div>
            <span><label>Screen</label></span>
            @foreach (var security in Model.SecurityTypes)
            { 
                <span><label>@security.Name</label></span>                    
            }
            <br />
            @foreach (var screen in Model.Screens)
            {
                <span>@screen.Name</span>
                foreach (var security in Model.SecurityTypes)
                { 
                    <span>@Html.CheckBoxFor(m => m.Permissions.Where(s => s.SecurityId == security.Id && s.ScreenId == screen.Id).Single().IsAllowed, new { @id = HtmlHelper.GenerateIdFromName("Create" + screen.Name + security.Name) })</span>                   
                }
                <br />
            }
        </div>
        <div>
            <span>
                <input type="image" src="/content/images/submit_button.png" value="submit" />
            </span> 
        </div>
        <div>
            <span id="addStatus" class="submitStatus"></span>
        </div>
    }

方法二:

 @using (Ajax.BeginForm("Create", "Role", null, new AjaxOptions { UpdateTargetId = "addStatus", InsertionMode = InsertionMode.Replace, OnSuccess = "onFormPostSuccess" }, new { @id = "AddForm" }))
    {  
        <div>
            <span><label>Screen</label></span>
            @foreach (var security in Model.SecurityTypes)
            { 
                <span><label>@security.Name</label></span>                    
            }
            <br />
            @foreach (int i=0; i < Model.Screens.Count(); i++)
            {
                <span>@Model.Screens.ElementAt(i).Name</span>
                foreach (int j=0; j<Model.SecurityTypes.Count(); j++)
                { 
                    @* The index 5*i+j is because I have 5 security types *@ 
                    <span>@Html.CheckBoxFor(Model.AllowedList[5*i+j], new { @id = HtmlHelper.GenerateIdFromName("Create" + @Model.Screens.ElementAt(i).Name + @Model.SecurityTypes.ElementAt(j).Name) })</span>                   
                }
                <br />
            }
        </div>
        <div>
            <span>
                <input type="image" src="/content/images/submit_button.png" value="submit" />
            </span> 
        </div>
        <div>
            <span id="addStatus" class="submitStatus"></span>
        </div>
    }

以下是 Controller 中 Create actionmethod 的代码:

    [Authorize]
    [HttpPost]
    public JsonResult Create(MyViewModel viewModel)
    {   
        if ( ModelState.IsValid)
        {                

            if (service.AddRole(viewModel))
            {                    
                return Json("Role Added !");
            }
            return Json("Role exists !");
        }
        return Json("Please correct errors");
    }

当我在 Create actionmethod 中检查 viewModel 时,所有 IsAllowed 属性都是错误的。正如在视图模型构造函数中初始化的那样。从视图中选中/取消选中复选框没有任何效果。

我错过了什么吗?

4

1 回答 1

8

像 CheckBoxFor 这样的强类型 HTML 助手只能使用简单的表达式作为第一个参数(最多访问属性和数组索引)。下面的表达式完全超出了这个助手的能力:

m => m.Permissions.Where(s => s.SecurityId == security.Id && s.ScreenId == screen.Id).Single().IsAllowed

我建议您在域模型和视图模型之间进行映射时使用真实的视图模型并在控制器级别执行此操作。


更新:

显然我对视图模型的回答不够清楚,所以让我试着举例说明我所说的视图模型的意思。

因此,我们首先定义实现该视图所需逻辑所需的视图模型:

public class CreateViewModel
{
    public IEnumerable<ScreenViewModel> Screens { get; set; }
}

public class ShowCreateViewModel: CreateViewModel
{
    public IEnumerable<SecurityTypeViewModel> SecurityTypes { get; set; }
}

public class ScreenViewModel
{
    public string ScreenName { get; set; }
    [HiddenInput(DisplayValue = false)]
    public int ScreenId { get; set; }
    public IEnumerable<SecurityTypeViewModel> SecurityTypes { get; set; }
}

public class SecurityTypeViewModel
{
    public string SecurityName { get; set; }
    [HiddenInput(DisplayValue = false)]
    public int SecurityTypeId { get; set; }
    public bool IsAllowed { get; set; }
}

然后我们可以有一个控制器动作来负责从存储库或其他东西中获取域模型并映射到视图模型:

public class HomeController : Controller
{
    public ActionResult Create()
    {
        // The information will obviously come from a domain model that 
        // we map to a view model, but for the simplicity of the answer
        // I am hardcoding the values here
        var securityTypes = new[]
        {
            new SecurityTypeViewModel { SecurityTypeId = 1, SecurityName = "security 1" },
            new SecurityTypeViewModel { SecurityTypeId = 2, SecurityName = "security 2" },
            new SecurityTypeViewModel { SecurityTypeId = 3, SecurityName = "security 3" },
        };

        // The information will obviously come from a domain model that 
        // we map to a view model, but for the simplicity of the answer
        // I am hardcoding the values here
        return View(new ShowCreateViewModel
        {
            SecurityTypes = securityTypes,
            Screens = new[]
            {
                new ScreenViewModel 
                {
                    ScreenId = 1,
                    ScreenName = "Screen 1",
                    SecurityTypes = securityTypes
                },
                new ScreenViewModel 
                {
                    ScreenId = 2,
                    ScreenName = "Screen 2",
                    SecurityTypes = securityTypes
                },
            }
        });
    }

    [HttpPost]
    public ActionResult Create(CreateViewModel model)
    {
        // The view model passed here will contain all the necessary information
        // for us to be able to perform the actual Save: 
        // a list of the screen ids along with a list of the selected permission ids

        return Content(
            "Thank you for selecting the following allowed permissions:<br/>" + 
            string.Join("<br/>", model.Screens.Select(s => string.Format(
                "screen id: {0}, permission ids: {1}", 
                s.ScreenId, 
                string.Join(",", s.SecurityTypes.Where(st => st.IsAllowed).Select(st => st.SecurityTypeId))
            )))
        );
    }
}

现在剩下的就是定义视图和相应的编辑器/显示模板。

让我们从主视图(~/Views/Home/Create.cshtml)开始:

@model ShowCreateViewModel

<script src="@Url.Content("~/Scripts/jquery.unobtrusive-ajax.js")" type="text/javascript"></script>

@using (Ajax.BeginForm("Create", "Home", null, new AjaxOptions { UpdateTargetId = "addStatus", InsertionMode = InsertionMode.Replace, OnSuccess = "onFormPostSuccess" }, new { @id = "AddForm" }))
{  
    <table>
        <thead>
            <tr>
                <th>Screen/Security type</th>
                @Html.DisplayFor(x => x.SecurityTypes)
            </tr>
        </thead>
        <tbody>
            @Html.EditorFor(x => x.Screens)
        </tbody>
    </table>    

    <div>
        <input type="submit" value="submit" />
    </div>

    <div id="addStatus" class="submitStatus"></div>
}

接下来我们有一个模型的编辑器模板ScreenViewModel~/Views/Shared/EditorTemplates/ScreenViewModel.cshtml):

@model ScreenViewModel
<tr>
    <td>
        @Html.DisplayFor(x => x.ScreenName)
        @Html.EditorFor(x => x.ScreenId)
    </td>
    @Html.EditorFor(x => x.SecurityTypes)
</tr>

然后是SecurityTypeViewModel模型的编辑器模板 ( ~/Views/Shared/EditorTemplates/SecurityTypeViewModel.cshtml):

@model SecurityTypeViewModel
<td>
    @Html.CheckBoxFor(x => x.IsAllowed)
    @Html.EditorFor(x => x.SecurityTypeId)
</td>

最后是模型的显示模板SecurityTypeViewModel( ~/Views/Shared/DisplayTemplates/SecurityTypeViewModel.cshtml):

@model SecurityTypeViewModel
<th>
    @Html.DisplayFor(x => x.SecurityName)
</th>

差不多就是这样:

在此处输入图像描述

我已经为你留下了你的实际领域模型和这里定义的视图模型之间的映射。

于 2012-07-13T12:26:52.190 回答