【问题标题】:@Html.CheckboxFor nor <input> passing checkbox value back to controller (binding issue)@Html.CheckboxFor 也不 <input> 将复选框值传递回控制器(绑定问题)
【发布时间】:2015-08-06 15:32:13
【问题描述】:

所以现在我正在尝试创建一个用户管理页面。页面的主要模型实际上是一个 IdentityUser,但我试图从视图中更新的模型是一个 IdentityRole

这是视图:

@model AG.SharedServices.DTO.IdentityUser

@{
    ViewBag.Title = "Edit";
    Layout = "~/Views/Shared/_Layout.cshtml";
}

<h2>Edit</h2>

@using (Html.BeginForm())
{
    @Html.AntiForgeryToken()

    <div class="form-horizontal">
        <h4>IdentityUser</h4>
        <hr />
        @Html.ValidationSummary(true, "", new { @class = "text-danger" })
        @Html.HiddenFor(model => model.Id)

        <div class="form-group">
            @Html.LabelFor(model => model.FirstName, htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EditorFor(model => model.FirstName, new { htmlAttributes = new { @class = "form-control" } })
                @Html.ValidationMessageFor(model => model.FirstName, "", new { @class = "text-danger" })
            </div>
        </div>

        <div class="form-group">
            @Html.LabelFor(model => model.LastName, htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EditorFor(model => model.LastName, new { htmlAttributes = new { @class = "form-control" } })
                @Html.ValidationMessageFor(model => model.LastName, "", new { @class = "text-danger" })
            </div>
        </div>
        <div class="form-group">
            <ul>
                @foreach (var role in TempData["Roles"] as IEnumerable<AG.SharedServices.DTO.IdentityRole>)
                {
                    <li>
                        <input name="@role.Name" type="checkbox" value="@role.Selected" />@role.Name
                    </li>
                }
            </ul>

        </div>


        <div class="form-group">
            <div class="col-md-offset-2 col-md-10">
                <input type="submit" value="Save" class="btn btn-default" />
            </div>
        </div>
    </div>
}

<div>
    @Html.ActionLink("Back to List", "Index")
</div>

我也试过用“@Html.CheckBoxFor(r =&gt; role.Selected)”替换“&lt;input type="submit" value="Save" class="btn btn-default" /&gt;”,但都没有传回复选框的值,它总是返回 false。 CheckBoxFor 不会接受任何其他值,而不是 IdentityUser,这是一个问题,如果我知道如何让它接受不同的模型会有所帮助

这是控制器。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Web;
using System.Web.Mvc;
using AG.SharedServices.DTO;
using System.Data;
using AG.BusinessServices;
using System.Net;
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.Owin;
using Microsoft.Owin.Security;

namespace AG.AGWeb.Controllers
{
    public class UserAdminController : Controller
    {
        private ApplicationUserManager _userManager;

        public UserAdminController()
        {

        }

        public UserAdminController(ApplicationUserManager userManager)
        {
            _userManager = userManager;
        }

        public ApplicationUserManager UserManager
        {
            get
            {
                return _userManager ?? HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>();
            }
            private set
            {
                _userManager = value;
            }
        }
        // GET: UserAdmin
        public ActionResult Index()
        {
            var users = UserManager.Users;
            return View(users);
        }

        // GET: UserAdmin/Details/5
        public ActionResult Details(int id)
        {
            return View();
        }

        // GET: UserAdmin/Create
        public ActionResult Create()
        {
            return View();
        }

        // POST: UserAdmin/Create
        [HttpPost]
        public ActionResult Create(FormCollection collection)
        {
            try
            {
                // TODO: Add insert logic here

                return RedirectToAction("Index");
            }
            catch
            {
                return View();
            }
        }

        // GET: UserAdmin/Edit/5
        public ActionResult Edit(string id)
        {
            var user = UserManager.FindById(id);
            var roleService = new RoleService();
            var roles = roleService.GetRoles();
            //add code to check users roles
            var userRoles = UserManager.GetRoles(id);
            foreach(var role in roles)
            {
                role.Selected = userRoles.Contains(role.Name);
            }
            TempData["Roles"] = roles;
            TempData.Keep("Roles");
            return View(user);
        }

        // POST: UserAdmin/Edit/5
        [HttpPost, ActionName("Edit")]
        [ValidateAntiForgeryToken]
        public async Task<ActionResult> Edit(IdentityUser model, string id)
        {
            try
            {
                // TODO: Add update logic here
                if (model == null)
                {
                    return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
                }
                if (ModelState.IsValid)
                {
                    var user = UserManager.FindById(id);
                    user.FirstName = model.FirstName;
                    user.LastName = model.LastName;
                    user.PhoneNumber = model.PhoneNumber;
                    var results = await UserManager.UpdateAsync(user);
                    if (results.Succeeded)
                    {
                        var roles = TempData["Roles"] as IEnumerable<IdentityRole>;
                        foreach(var role in roles)
                        {
                            if (role.Selected && await UserManager.IsInRoleAsync(id, role.Id) == false)
                            {
                                await UserManager.AddToRoleAsync(id, role.Id);
                            }
                            else
                            {
                                await UserManager.RemoveFromRoleAsync(id, role.Id);
                            }
                        }
                    }

                }

                    return RedirectToAction("Index");
            }
            catch
            {
                return View();
            }
        }

        // GET: UserAdmin/Delete/5
        public ActionResult Delete(string id)
        {
            var user = UserManager.FindById(id);
            return View(user);
        }

        // POST: UserAdmin/Delete/5
        [HttpPost]
        public ActionResult Delete(int id, FormCollection collection)
        {
            try
            {
                // TODO: Add delete logic here

                return RedirectToAction("Index");
            }
            catch
            {
                return View();
            }
        }
    }
}

我们试图传递的模型是 IdentityRole,看起来像这样:

using Microsoft.AspNet.Identity;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace AG.SharedServices.DTO
{
    public class IdentityRole : IRole
    {
        /// <summary>
        /// Default constructor for Role 
        /// </summary>
        public IdentityRole()
        {
            Id = Guid.NewGuid().ToString();
        }
        /// <summary>
        /// Constructor that takes names as argument 
        /// </summary>
        /// <param name="name"></param>
        public IdentityRole(string name)
            : this()
        {
            Name = name;
        }

        public IdentityRole(string name, string id)
        {
            Name = name;
            Id = id;
        }

        /// <summary>
        /// Role ID
        /// </summary>
        public string Id { get; set; }

        /// <summary>
        /// Role name
        /// </summary>
        public string Name { get; set; }

        public bool Selected { get; set; }
    }
}

有没有人知道如何在视图中使用 foreach 循环来传递 IdentityRoles 并更改角色的 Selected 值以便将用户分配给角色?

谢谢!

【问题讨论】:

  • 您没有在视图中的任何地方使用CheckBoxFor()(查看您的问题标题)。您只需创建一个名称与您的模型完全没有关系的复选框集合,因此它们永远不会绑定到任何东西。使用代表您需要编辑的视图模型。
  • 查看最近的编辑,我添加了我们尝试与CheckBoxFor()一起使用的代码
  • 没什么区别 - 您不会生成与您的模型相关的输入。而且在任何情况下,您都不能使用foreach 循环(它必须是for 循环)

标签: c# html asp.net-mvc razor


【解决方案1】:

你应该在你的动作中使用像 UserIdentityModel 这样的角色数组

public class UserIdentityModel
{
   ...

   public IList<IdentityRole> Roles { get; set; }
}

并在您的视图上创建角色列表

   @model UserIdentityModel
   ...
   <ul>
       @for (var i = 0; i < Model.Roles.Count; i++)
       {
          <li>
             <input name="@("Roles[" + i + "].Selected")" type="checkbox" value="@(Model.Roles[i].Selected)" />@Model.Roles[i].Name
             <input type="hidden" name="@("Roles[" + i + "].Name")" value="@(Model.Roles[i].Name)">
             <input type="hidden" name="@("Roles[" + i + "].Id")" value="@(Model.Roles[i].Id)">
          </li>
       }
    </ul>

在您的发布操作中,您将收到带有角色集合的 UserIdentityModel

public ActionResult Edit(string id)
{
   var user = UserManager.FindById(id);
   var model = new UserIdentityModel();
   ///Fill all fields of the model      
   return View(model);
}

// POST: UserAdmin/Edit/5
[HttpPost, ActionName("Edit")]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Edit(UserIdentityModel model)
{
   ...
   foreach(var role in model.Roles)
   {
      ...
   }

【讨论】:

  • 这实际上是我们最终做的!不过谢谢你,很好的答案!
  • 生成 html 的糟糕方法 - :) 使用 @Html.HiddenFor(m =&gt; m.Roles[i].Name)。但如果 OP 真的为集合生成大量隐藏输入,那么它只会降低性能并导致过度发布攻击。
猜你喜欢
  • 2020-06-07
  • 1970-01-01
  • 2018-12-15
  • 2016-01-31
  • 1970-01-01
  • 2017-09-01
  • 1970-01-01
  • 1970-01-01
  • 2014-08-24
相关资源
最近更新 更多