【问题标题】:Mvc form with viewmodel properties contain list<object>具有 viewmodel 属性的 Mvc 表单包含 list<object>
【发布时间】:2016-10-12 14:15:57
【问题描述】:

我是 MVC5 的新手。我想将建筑细节添加到数据库中。建筑物详细信息包括名称、房间详细信息列表等。用户可以添加建筑物详细信息,例如名称、房间详细信息列表。添加所有详细信息后,房间和建筑物的详细信息列表将保存到数据库中。

Add Building Details Image

我有如下视图模型的表单

BuildingViewModel.cs

public class Building
{
    public Building()
    {
        Rooms = new List<Room>();
        NewRoom = new Room();
    }

    [Required]
    public string Name { get; set; }

    public List<Room> Rooms { get; set; }

    public Room NewRoom { get; set; }

    public string Button { get; set; }
}

public class Room
{
    [Required]
    public string RoomName { get; set; }

    public string Area { get; set; }
}

这是我的视图 Create.cshtml

@model Building
@{
    ViewBag.Title = "Create";
}

@using (Html.BeginForm())
{
    <h2>Create</h2>

    <h3>Building</h3>
    @Html.LabelFor(t => t.Name)
    @Html.TextBoxFor(t => t.Name)
    @Html.ValidationMessageFor(t => t.Name)  

    <br />
    <br />

    <fieldset>
        <legend>Room</legend>

        <div class="editor-label">
            @Html.LabelFor(model => model.NewRoom.RoomName)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.NewRoom.RoomName)
            @Html.ValidationMessageFor(model => model.NewRoom.RoomName)
        </div>

        <div class="editor-label">
            @Html.LabelFor(model => model.NewRoom.Area)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.NewRoom.Area)
            @Html.ValidationMessageFor(model => model.NewRoom.Area)
        </div>

        <p>
            <input type="submit" name="button" value="Add Room" />
        </p>
    </fieldset>

    <br />
    <table border="1" cellpadding="5px" cellspacing="0">
        <thead>
            <tr>
                <th>#</th>
                <th>Name</th>
                <th>Description</th>
            </tr>
        </thead>
        <tbody>
            @for (int i = 0; i < Model.Rooms.Count(); i++)
            {
                var room = Model.Rooms[i];
                <tr>
                    <td>@(i + 1)</td>
                    <td>@room.RoomName @Html.HiddenFor(t => room.RoomName)</td>
                    <td>@room.Area @Html.HiddenFor(t => room.Area)</td>
                </tr>
            }
        </tbody>
    </table>
    <br />

    <input type="submit" name="button" value="Create" />
}
<script src="~/Scripts/jquery-1.8.0.min.js"></script>
<script src="~/Scripts/jquery.validate.min.js"></script>
<script src="~/Scripts/jquery.validate.unobtrusive.min.js"></script>

这是我的控制器 BuildingController.cs

public class BuildingController : Controller
{
    [HttpGet]
    public ActionResult Create()
    {
        var building = new Building();
        return View(building);
    }

    [HttpPost]
    public ActionResult Create(Building building)
    {
        switch (building.Button)
        {
            case "Add Room":
                if (ModelState.IsValid)
                {
                    building.Rooms.Add(building.NewRoom);
                    building.NewRoom = new Room();

                    return View(building);
                }
                break;

            case "Create":
                if (ModelState.IsValid)
                {
                    // TODO: save to db

                    return View();
                }
                break;
        }

        return View(building);
    }
}

我的问题是,当单击“添加房间”按钮时,它会在建筑物的名称属性中引发验证错误。我想要的是,仅在单击“添加房间”按钮时显示对房间类属性的验证,并在单击“创建”按钮时显示建筑属性的验证错误。

Add Building Validation Error Image

我为这个问题花费了超过 2 周的时间。请帮帮我...

感谢您宝贵的时间。

【问题讨论】:

  • 您采用了错误的方法,并且这些代码都没有任何意义(并且您的 @for (int i = 0; i &lt; Model.Rooms.Count(); i++) 循环中的代码正在生成与您的模型无关且不会绑定到任何东西的 name 属性. 您可以通过在一个操作中编辑Building 及其Room 集合来处理此问题(请参阅this answer),这允许您动态添加新的集合项。
  • 或者您以一种形式创建建筑物,一旦创建,您就重定向到视图以创建其房间。但是,您为什么要创建与相关建筑物没有关系的 Room 对象尚不清楚。你Room 类需要Building 的导航属性,否则数据毫无意义

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


【解决方案1】:

由于您的属性Building.Name(即[Required])而引发验证错误。

因此您只需删除[Require] 属性和@Html.ValidationMessageFor(t =&gt; t.Name)

【讨论】:

  • OP 希望在单击“创建”按钮时显示验证。如果您取消 [Required] 属性,那么 OP 将无法获得他们正在寻找的验证。
  • @BviLLe_Kid 我的问题是,当点击“添加房间”按钮时,它会在建筑物的名称属性中引发验证错误。这是否意味着他不喜欢验证Building.Name?
  • @BviLLe_Kid 是对的...我希望在单击“创建”按钮时出现 Building.Name 验证错误。
【解决方案2】:

您在控制器方法中做了很多工作。我会将您的页面拆分为 2 个具有自己的视图模型的部分视图。因为它们是具有不同数据要求的 2 个不同的函数。

每个部分视图都应包含每个操作的单独表单。然后将每个表单的提交按钮映射到控制器上的单独操作。

【讨论】:

  • 我尝试了您的解决方案,但我必须保持设计不变。
  • 如何在不创建建筑物的情况下创建房间?
  • 在单页中我想添加带有房间的建筑细节(希望包含的图像是自我解释的。)
  • 好的。不要使用 2 个局部视图,而是在页面上创建 2 个表单,这些表单使用不同的视图模型路由到不同的控制器操作。
【解决方案3】:

最后我找到了使用局部视图和 Ajax URL 发布的解决方案。

这是解决方案:-)

BuildingController.cs

public class BuildingController : Controller
{
    [HttpGet]
    public ActionResult Create()
    {
        var building = new Building();
        return View(building);
    }

    [HttpPost]
    public ActionResult Create(Building building)
    {
        switch (building.Button)
        {
            case "Add Room":
                if (ModelState.IsValid)
                {
                    building.Rooms.Add(building.NewRoom);
                    building.NewRoom = new Room();
                    ModelState.Clear();
                    return PartialView("_Room", building);
                }
                break;

            case "Create":
                if (ModelState.IsValid)
                {
                    // TODO: save to db

                    return Json("Building created successfully.", JsonRequestBehavior.AllowGet);
                }
                break;
        }

        return View(building);
    }
}

BuildingViewModel.cs

public class Building
{
    public Building()
    {
        Rooms = new List<Room>();
        NewRoom = new Room();
    }

    public string Button { get; set; }

    //[Required(AllowEmptyStrings = false, ErrorMessage = "The building name is required.")]
    public string Name { get; set; }

    public Room NewRoom { get; set; }
    public List<Room> Rooms { get; set; }
}

public class Room
{
    //[Required(AllowEmptyStrings = false, ErrorMessage = "The room name is required.")]
    public string RoomName { get; set; }

    public string Area { get; set; }
}

创建.cshtml

@model Building
@{
    ViewBag.Title = "Create";
 }

 @using (Html.BeginForm("Create", "Building", FormMethod.Post, new { @id = "commentForm" }))
{
<h2>Create</h2>

<h3>Building</h3>
@Html.LabelFor(t => t.Name)
@Html.TextBoxFor(t => t.Name, htmlAttributes: new { @class = "create" })
@Html.ValidationMessageFor(t => t.Name)  

<br />
<br />

<div id="rooms">
    @Html.Partial("_Room", Model)
</div>

<br />

<button onclick="create()" >Create</button>
}
<script src="~/Scripts/jquery-1.8.2.min.js"></script>
<script src="~/Scripts/jquery.validate.min.js"></script>
<script src="~/Scripts/jquery.validate.unobtrusive.min.js"></script>

<script>
$(document).ready(function () {

    $(".create").removeAttr('required');
    $(".add-room").removeAttr('required');
});

function addRoom() {
    debugger;
    $(".add-room").removeAttr('required');
    $(".add-room").attr('required', true);

    var $valid = true;
    $('.add-room').each(function (i, obj) {
        var a = $(this).valid();
        if (!a) {
            $valid = a;
        }
    });

    if (!$valid) {
        return false;
    }
    else {

        var data = $("#commentForm").serializeArray();
        data.push({ name: 'Button', value: 'Add Room' });

        $.ajax({
            url: '@Url.Action("Create", "Building")',
            type: 'post',
            data: data,
            success: function (data) {
                debugger;
                $('#rooms').html(data);

                $(".add-room").removeAttr('required');
            },
            error: function (xhr, status, error) {
                debugger;
                alert(error);
            }
        });

    }
}

function create() {

    $(".add-room").removeAttr('required');
    $(".create").removeAttr('required');
    $(".create").attr('required', true);

    var $valid = true;
    $('.create').each(function (i, obj) {
        var a = $(this).valid();
        if (!a) {
            $valid = a;
        }
    });

    if (!$valid) {
        return false;
    }
    else {
        debugger;
        var data = $("#commentForm").serializeArray();
        data.push({ name: 'Button', value: 'Create' });

        $.ajax({
            url: '@Url.Action("Create", "Building")',
            type: 'post',
            data: data,
            success: function (data) {
                debugger;
                alert(data);
            },
            error: function (xhr, status, error) {
                debugger;
                alert(error);
            }
        });

    }
}
</script>

最后是部分视图_Room.cshtml

@model Building

<fieldset>
<legend>Room</legend>

<div class="editor-label">
    @Html.LabelFor(model => model.NewRoom.RoomName)
</div>
<div class="editor-field">
    @Html.TextBoxFor(model => model.NewRoom.RoomName, htmlAttributes: new { @class = "add-room" })
    @Html.ValidationMessageFor(model => model.NewRoom.RoomName)
</div>

<div class="editor-label">
    @Html.LabelFor(model => model.NewRoom.Area)
</div>
<div class="editor-field">
    @Html.TextBoxFor(model => model.NewRoom.Area, htmlAttributes: new { @class = "add-room" })
    @Html.ValidationMessageFor(model => model.NewRoom.Area)
</div>

<p>
    <button onclick="addRoom();">Add Room</button>
</p>
</fieldset>

<br />
<table border="1" cellpadding="5px" cellspacing="0">
<thead>
    <tr>
        <th>#</th>
        <th>RoomName</th>
        <th>Area</th>
    </tr>
</thead>
<tbody>
    @for (int i = 0; i < Model.Rooms.Count(); i++)
    {
        <tr>
            <td>@(i + 1)</td>
            <td>@Model.Rooms[i].RoomName @Html.HiddenFor(t => Model.Rooms[i].RoomName)</td>
            <td>@Model.Rooms[i].Area @Html.HiddenFor(t => Model.Rooms[i].Area)</td>
        </tr>
    }
</tbody>
</table>

谢谢大家。

【讨论】:

    猜你喜欢
    • 2016-01-20
    • 1970-01-01
    • 1970-01-01
    • 2015-10-09
    • 2020-11-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多