Подтвердить что ты не робот

Создание нового родителя и дочернего элемента на одной странице

В моем приложении MVC есть классические отношения родитель-потомок (master-detail).

Я хочу иметь одну страницу, которая создаст как родителя, так и детей на одной странице. Я добавил действие, которое возвращает частичное представление, которое добавляет дочерний HTML в представление родителей, но я не знаю, как связать вновь созданный ребенок в действии с исходным родителем (другими словами, как добавить новый дочернего объекта для сбора этих объектов в родительском объекте).

Я предполагаю, что при отправке формы действие должно получить родительский объект с вновь созданными дочерними элементами в его коллекции.

Итак, чтобы сделать что-то не так, каков должен быть код действия, создающего дочерний объект и как дочерний элемент добавляется в его родительскую коллекцию?

Я прочитал много сообщений здесь (и других сайтов) и не смог найти пример.

В приложении используются MVC 4 и Entity Framework 5.

Пример кода (я удалил часть кода, чтобы она была простой). Модель представляет собой формы (родительские) и объекты 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());
}

Спасибо заранее,

Sharon.

4b9b3361

Ответ 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>

Вызывая это частичное представление в представлении create place ajaxly, вы можете отображать некоторые элементы для каждого тега. Каждая строка элементов содержит метку, теги DropDownList и кнопку удаления, чтобы просто удалить созданные элементы.

В представлении create place у вас есть голая таблица, которая будет содержать те элементы, которые вы создаете с помощью частичного представления:

<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>

и у вас есть следующий script, чтобы создать строку элементов для каждого тега:

$(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");
    }

и ваш результат для создания... Все решение для вашего вопроса имеет много кодов для просмотров, частичных представлений, контроллеров, аякс-вызовов и привязки к модели. Я пытался просто показать вам путь, потому что я действительно не могу опубликовать их в этом ответе.

Здесь - полная информация и инструкции.

Надеюсь, что этот ответ будет полезен и поможет вам.

Ответ 2

Вы можете использовать html-метод @Html.RenderPartial(_CreateFormField.cshtml) внутри вашей родительской страницы cshtml.

Для информации о режиме http://msdn.microsoft.com/en-us/library/dd492962(v=vs.118).aspx

Я предоставляю пример псевдокода,

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

Пожалуйста, попробуйте его в правильном С# синтаксическом ключе, и вы получите частичные представления, отображаемые для каждого элемента коллекции.

Ответ 3

Проблема заключается в том, что ваше частичное представление должно использовать:

@model Form //Instead of FormField

а затем внутри частичного представления вы должны использовать модель 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>

Это работает только для отношений "один-к-одному" (или так я читаю в этом потоке: здесь). Также не имеет значения, есть ли у вас @Html.Form внутри частичного представления.

После внесения этих изменений у меня не возникло проблем с возвратом всех опубликованных данных в контроллер.

РЕДАКТИРОВАТЬ: У меня возникли проблемы с этим, как скоро кто-то выяснит.

Лучший способ сделать это (что я видел) вместо этого использовать EditorTemplate. Таким образом, редактор остается независимым от родителя, и вы можете добавить форму на любую страницу, а не просто страницу, где вы также добавляете FormField.

Затем вы заменили

@Html.Partial("view")

с

@Html.EditorFor()

В принципе, я выяснил, что лучше использовать @Html.EditorFor вместо частичного представления при редактировании (это называется представлением, а не редактором).

Ответ 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>

Надеюсь, что это поможет вам.