【问题标题】:Create new parent and child on the same page在同一页面上创建新的父级和子级
【发布时间】:2013-01-27 15:08:35
【问题描述】:

我的 MVC 应用程序具有经典的父子(主从)关系。

我想要一个页面,在同一页面上创建新的父级和子级。我添加了一个返回部分视图并将子 HTML 添加到父视图的操作,但我不知道如何将操作中新创建的子级关联到原始父级(换句话说,我如何添加新的子实体到父实体中这些实体的集合)。

我猜当我提交表单时,该操作应该获取父实体及其集合中新创建的子实体。

所以简而言之,创建子实体的操作代码应该是什么,以及如何将子实体添加到其父集合中?

我在这里(和其他网站)阅读了很多帖子,但找不到示例。

该应用程序使用 MVC 4 和 Entity Framework 5。

代码示例(我删除了一些代码以保持简单)。 该模型是 Form(父)和 FormField(子)实体。

public partial class Form
{ 
    public int ID { get; set; }
    public string Name { get; set; }

    public virtual ICollection<FormField> FormFields { get; set; }
}

public partial class FormField
{
    public int ID { get; set; }
    public string Name { get; set; }
    public int FormID { get; set; }
}

以下部分视图(_CreateFormField.cshtml)创建新的 FormField(子)。

@model FormField

@using (Html.BeginForm()) {
@Html.ValidationSummary(true)

<fieldset>
    <legend>FormField</legend>

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

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


</fieldset>
}

以下视图(Create.cshtml)是创建表单的视图。

@model Form

@using (Html.BeginForm()) {
@Html.ValidationSummary(true)

<fieldset>
    <legend>Form</legend>

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

<div>
        @Html.ActionLink(
                "Add Field",
                "CreateFormField", 
                new { id = -1},
                new { @class = "form-field" })
    </div>
    <p>
        <input type="submit" value="Create" />
    </p>
</fieldset>
}

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

<div id="CreateFormField"></div>

@section Scripts {
<script>
    $(function () {
        $('.form-field').on('click', function (e) {

            $.get($(this).prop('href'), function (response) {
                $('#CreateFormField').append(response)
            });

            e.preventDefault();
        });
    });
</script>

@Scripts.Render("~/bundles/jqueryval")

}

以下操作处理 FormController 中的创建。

[HttpPost]
public ActionResult Create(Form form)
{
    if (ModelState.IsValid)
    {
        db.Forms.Add(form);
        db.SaveChanges();
        return RedirectToAction("Index");
    }

    return View(form);
}

public ActionResult CreateFormField(string id = null)
{
    // I guess something is missing here.

    return PartialView("_CreateFormField", new FormField());
}

提前致谢,

莎朗。

【问题讨论】:

  • 您能展示一下您目前拥有的代码吗?用具体的代码示例进行解释会更好。
  • 我注意到的一个问题是局部视图包含一个表单。我已经删除了它,但它并没有改变问题。
  • 有人吗?这应该是一个非常常见的场景。

标签: asp.net-mvc entity-framework asp.net-mvc-4 entity-framework-5 master-detail


【解决方案1】:

我认为对你来说最好和最简单的方法是你有一个创建Form的视图,并在它的底部放置一个fieldset来分配FormFields给它。

对于字段集,您应该有两个部分视图:一个用于创建,另一个用于编辑。创建的局部视图应该是这样的:

@model myPrj.Models.Form_FormFieldInfo

@{ 
    var index = Guid.NewGuid().ToString(); 
    string ln = (string)ViewBag.ListName;
    string hn = ln + ".Index";
}

<tr>
    <td>        
        <input type="hidden" name="@hn" value="@index" />
        @Html.LabelFor(model => model.FormFieldID)
    </td>
    <td>
        @Html.DropDownList(ln + "[" + index + "].FormFieldID", 
            new SelectList(new myPrj.Models.DbContext().FormFields, "ID", "FieldName"))
    </td>        
    <td>
        <input type="button" onclick="$(this).parent().parent().remove();" 
            value="Remove" />
    </td>
</tr>

通过ajaxly在create place view中调用这个局部视图,你可以为每个标签渲染一些元素。每行元素都包含一个标签、一个包含标签的 DropDownList 和一个删除按钮,用于简单地删除创建的元素。

在创建位置视图中,您有一个裸表,其中包含您通过局部视图创建的那些元素:

<fieldset>
     <legend>Form and FormFields</legend>
     @Html.ValidationMessageFor(model => model.FormFields)</label>
     <table id="tblFields"></table>                                        
     <input type="button" id="btnAddTag" value="Add new Field"/>
     <img id="imgSpinnerl" src="~/Images/indicator-blue.gif" style="display:none;" />
</fieldset>

你有以下脚本来为每个标签创建一行元素:

$(document).ready(function () {
    $("#btnAddField").click(function () {
        $.ajax({
            url: "/Controller/GetFormFieldRow/FormFields",
            type: 'GET', dataType: 'json',
            success: function (data, textStatus, jqXHR) {
                $("#tblFields").append(jqXHR.responseText);
            },
            error: function (jqXHR, textStatus, errorThrown) {
                $("#tblFields").append(jqXHR.responseText);
            },
            beforeSend: function () { $("#imgSpinnerl").show(); },
            complete: function () { $("#imgSpinnerl").hide(); }
        });
    });
});

动作方法GetFormFieldRow如下:

public PartialViewResult GetFormFieldRow(string id = "")
    {            
        ViewBag.ListName = id;
        return PartialView("_FormFieldPartial");
    }

您已经完成了创建...您的问题的整个解决方案有许多用于视图、部分视图、控制器、ajax 调用和模型绑定的代码。我试图向您展示方式,因为我真的无法在此答案中发布所有这些。

Here 是完整的信息和操作方法。

希望这个答案对您有用并为您指明方向。

【讨论】:

    【解决方案2】:

    您可以在父 cshtml 页面中使用 @Html.RenderPartial(_CreateFormField.cshtml) htlper 方法。

    模式信息,http://msdn.microsoft.com/en-us/library/dd492962(v=vs.118).aspx

    我提供一个伪代码示例,

    @Foreach(childModel in model.FormFields)
    {
        @Html.RenderPartial("childView.cshtml","Name", childModel)
    }
    

    请以正确的 c# 语法方式尝试一下,您将获得为每个集合项呈现的部分视图。

    【讨论】:

      【解决方案3】:

      问题是你的局部视图需要使用:

      @model Form //Instead of FormField
      

      然后在局部视图内部,您必须使用 model => model.FormField.x

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

      这只适用于一对一的关系(或者我在这个线程中读到:Here)。部分视图中是否有 @Html.Form 也没关系。

      进行这些更改后,我可以毫无问题地将所有发布的数据返回到控制器。

      编辑:我遇到了这个问题,任何人很快就会发现。

      更好的方法(我已经看到)是使用 EditorTemplate。这样编辑器就保持独立于父级,您可以在任何页面上添加 Form,而不仅仅是在其中添加 FormField 的页面。

      然后你会替换

      @Html.Partial("view")
      

      @Html.EditorFor()
      

      基本上,我发现在进行编辑时使用@Html.EditorFor 而不是局部视图要好得多(它被称为视图 - 不是编辑器)。

      【讨论】:

        【解决方案4】:

        如果你想使用基于网格的布局,你可以试试Kendo UI grid

        【讨论】:

          【解决方案5】:

          使用jQuery在点击按钮时添加FormField。

          类似的我用过如下

           <div class="accomp-multi-field-wrapper">
           <table class="table">
              <thead>
                  <tr>
                      <th>Name</th>
                      <th>FormId</th>
                      <th>Remove</th>
                     </tr>
               </thead>
          <tbody class="accomp-multi-fields">
           <tr class="multi-field">
                  <td> <input name="FormFields[0].Name" type="text" value=""></td>
                  <td> <input name="FormFields[0].FormId" type="text" value=""></td>
                  <td> <button type="button" class="remove-field">X</button></td>
               </tr> 
          
          </tbody>
          <tr>
              <th><button type="button" class="add-field btn btn-xs btn-primary addclr"><i class="fa fa-plus"></i> Add field</button> </th>
          </tr>
          </table>
          </div>
          

          和jQuery的添加和删除字段如下

           <script>
           $('.accomp-multi-field-wrapper').each(function () {
                    var $wrapper = $('.accomp-multi-fields', this);
                    $(".add-field", $(this)).click(function (e) {
                    var $len = $('.multi-field', $wrapper).length;
                    $('.multi-field:first-child', $wrapper).clone(false, true).appendTo($wrapper).find('select,input,span').val('').each(function () {
                    $(this).attr('name', $(this).attr('name').replace('\[0\]', '\[' + $len + '\]'));
              });
          
          $(document).on("click",'.multi-field .remove-field', function () {
                      if ($('.multi-field', $wrapper).length > 1) {
                          $(this).closest('tr').remove();
                          //
                          $('.multi-field', $wrapper).each(function (indx) {
                              $(this).find('input,select,span').each(function () {
                                $(this).attr('name', $(this).attr('name').replace(/\d+/g, indx));
          
                              })
                          })
                      }
                  });
              </script>
          

          希望这会对你有所帮助。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2015-05-17
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多