Question Créer un nouveau parent et un enfant sur la même page


Mon application MVC a des relations classiques parent-enfant (maître-détail).

Je veux avoir une seule page qui crée à la fois le nouveau parent et les enfants sur la même page. J'ai ajouté une action qui renvoie une vue partielle qui ajoute le code HTML enfant à la vue du parent, mais je ne sais pas comment associer l'enfant nouvellement créé à l'action au parent d'origine (en d'autres termes, comment ajouter la nouvelle entité enfant à la collection de ces entités dans l'entité parent).

Je suppose que lorsque je soumets le formulaire, l'action doit avoir l'entité parent avec les enfants nouvellement créés dans sa collection.

Donc, pour faire court, quel devrait être le code de l'action qui crée l'entité enfant et comment l'enfant est-il ajouté à sa collection parente?

J'ai lu beaucoup de messages ici (et d'autres sites) et je n'ai pas pu trouver d'exemple.

L'application utilise MVC 4 et Entity Framework 5.

Échantillon de code (j'ai supprimé une partie du code pour le garder simple). Le modèle est constitué des entités Form (parent) et FormField (enfant).

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

La vue partielle suivante (_CreateFormField.cshtml) crée un nouveau FormField (enfant).

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

Et la vue suivante (Create.cshtml) est celle qui crée le formulaire.

@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")

}

Les actions suivantes gèrent la création dans le 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());
}

Merci d'avance,

Sharon.


12
2018-01-27 15:08


origine


Réponses:


Je pense que le meilleur et le plus simple pour vous est que vous avez une vue pour créer Form et au fond de cela mettre un fieldset assigner FormFields à elle.

Pour les champs, vous devez avoir deux vues partielles: une pour la création et une autre pour la modification. La vue partielle pour la création devrait être quelque chose comme ceci:

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

En appelant cette vue partielle dans la vue créer un lieu ajaxly, vous pouvez rendre certains éléments pour chaque balise. Chaque ligne d'éléments contient une étiquette, un DropDownList contenant des balises et un bouton de suppression pour supprimer simplement les éléments créés.

Dans la vue Créer un lieu, vous avez une table nue qui contiendra les éléments que vous créez via la vue partielle:

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

et vous avez le script suivant pour créer une ligne d'éléments pour chaque balise:

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

La méthode d'action GetFormFieldRow est comme suit:

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

et votre fait pour la création ... Toute la solution pour votre question a beaucoup de codes pour les vues, les vues partielles, les contrôleurs, les appels ajax et la liaison de modèle. J'ai essayé de vous montrer le chemin car je ne peux vraiment pas les afficher tous dans cette réponse.

Ici est l'info complète et comment faire.

J'espère que cette réponse sera utile et vous guidera.


1
2017-07-27 12:42



Vous pouvez utiliser la méthode htlper @ Html.RenderPartial (_CreateFormField.cshtml) dans votre page parent cshtml.

Pour les informations sur le mode, http://msdn.microsoft.com/en-us/library/dd492962(v=vs.118).aspx

Je fournis un exemple de pseudo-code,

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

S'il vous plaît, essayez-le de manière syntaxique et vous obtiendrez les vues partielles pour chaque élément de la collection.


1
2017-12-15 12:41



Le problème est que votre vue partielle doit utiliser:

@model Form //Instead of FormField

et ensuite à l'intérieur de la vue partielle, vous devez utiliser 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>

Cela ne fonctionne que pour les relations un-à-un (ou alors j'ai lu dans ce fil: Ici). Peu importe si vous avez @ Html.Form dans la vue partielle.

Après avoir apporté ces modifications, je n'ai eu aucun problème pour récupérer toutes les données postées sur le contrôleur.

MODIFIER: J'ai eu des problèmes avec cela, comme tout le monde le comprendrait bientôt.

La meilleure façon de faire cela (que j'ai vu) est d'utiliser un EditorTemplate. De cette façon, l'éditeur reste indépendant du parent et vous pouvez ajouter un Forme sur n'importe quelle page, pas simplement une page où vous ajoutez également un FormField.

Vous remplaceriez alors

@Html.Partial("view")

avec

@Html.EditorFor()

Fondamentalement, j'ai découvert qu'il est préférable d'utiliser un @ Html.EditorFor au lieu d'une vue partielle lors de l'édition (c'est ce qu'on appelle une vue - pas un éditeur).


0
2018-06-06 17:22



Si vous souhaitez utiliser une mise en page basée sur une grille, vous pouvez essayer Grille de Kendo UI


0
2018-01-07 16:49



Utilisez jQuery pour ajouter FormField en cliquant sur le bouton.

Similaire je l'ai utilisé comme suit

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

et jQuery est comme suit pour ajouter un champ et supprimer

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

J'espère que cela va vous aider.


0
2018-02-07 11:38