How to create master-detail form using a Viewmodel

Asked

Viewed 487 times

2

I have a viewmodel in my project. Within it I have two entities that are a list of items.

I’m doing the data editing logic of these entities that are inside the viewmodel. I have 5 entities in all. Those that are not a list, I can popular the data in the relevant fields in view, now, the list ones I can’t. I can load the object and fill it, but I can’t pass this data to the view.

The codes I have are:

    public ActionResult Edit(int? id)
    {
        CliCliente cliente = db.CliCliente.Find(id);
        AnaAnamineseAlimentar anamnese = db.AnaAnamineseAlimentar.Find(id);
        RecRecordatorio recordatorio = db.RecRecordatorio.Find(id);
        List<RefRefeicao> refeicao = anamnese.RefRefeicao; //db.RefRefeicao.Find(id);
        List<QfaQuestionarioFrequenciaAlimentar> qfa = anamnese.QfaQuestionarioFrequenciaAlimentar;//= db.QfaQuestionarioFrequenciaAlimentar.Find(id);

        for (int i = 0; i < qfa.Count; i++)
        {
            qfa[i].AnaId = anamnese.AnaId;
            qfa[i].AnaAnamineseAlimentar = anamnese;
        }

        for (int i = 0; i < refeicao.Count; i++)
        {
            refeicao[i].AnaId = anamnese.AnaId;
            refeicao[i].AnaAnamineseAlimentar = anamnese;
        }

        AnamineseViewModel viewModel = new AnamineseViewModel() 
        {
            CliCliente = cliente,
            AnaAnamineseAlimentar = anamnese,
            RecRecordatorio = recordatorio,
            RefRefeicao = refeicao,
            QfaQuestionarioFrequenciaAlimentar = qfa
        };

        return View(viewModel);
    }

In the view:

@model NutriSport.Models.AnamineseViewModel

<div class="form-horizontal">
    <br />

    @Html.ValidationSummary(true, "", new { @class = "text-danger" })

    <table class="table">

        <tr class="success">
            <th>
                Tipo
            </th>

            <th>
                Horário/Local
            </th>

            <th>
                Alimentos/Quantidades
            </th>
        </tr>

        @for (int i = 0; i <= Model.RefRefeicao.Count; i++)
        {
            <tr class="success">

                <td>
                    @Html.TextBox(string.Format("RefRefeicao[{0}].RefTipo", i), null, new { @class = "form-control" })
                </td>

                <td>
                    @Html.TextBox(string.Format("RefRefeicao[{0}].RefHorarioLocal", i), null, new { @class = "form-control" })
                </td>

                <td>
                    @Html.TextArea(string.Format("RefRefeicao[{0}].RefAlimentosQuantidades", i), null,  new { @class = "form-control" })
                </td>
            </tr> 
        }
    </table>
</div>

1 answer

2


In fact you can pass the data to the View yes. The problem is another: you are trying to put together a master-detail form and possibly not getting the way you need to get.

The principle of assembly is correct. There is a need to increase the intelligence of your code so that the form makes sense.

If it is a form, you need to write it down as one, ie:

@model NutriSport.Models.AnamineseViewModel

@using (Html.BeginForm())
{
    <div class="form-horizontal">
        <br />

        @Html.ValidationSummary(true, "", new { @class = "text-danger" })
        ... 

The next detail is how you write the form. You did it in the way that is correct, but unnecessarily laborious. In addition, to work properly the Binding of this form, you would need to indicate to Binder that the current record is index i. That is to say:

    @for (int i = 0; i <= Model.RefRefeicao.Count; i++)
    {
        <tr class="success">
            <input type="hidden" name="RefRefeicao.index" id="RefRefeicao_index" value="@i.ToString()" /> @* Isto aqui *@
            <td>
                @Html.TextBox(string.Format("RefRefeicao[{0}].RefTipo", i), null, new { @class = "form-control" })
            </td>

            <td>
                @Html.TextBox(string.Format("RefRefeicao[{0}].RefHorarioLocal", i), null, new { @class = "form-control" })
            </td>

            <td>
                @Html.TextArea(string.Format("RefRefeicao[{0}].RefAlimentosQuantidades", i), null,  new { @class = "form-control" })
            </td>
        </tr> 
    }

Done so, the Model Binder correctly identifies your element as an aggregate class object RefRefeicao.

Only, as I’ve said a few times, there is a component that solves this for you. I’ve talked about him many, many times. With it, you can assemble your list for the form as follows:

    @foreach (var refeicao in Model.RefRefeicao)
    {
        @Html.Partial("_Refeicoes", refeicao)
    }

In this case, you need to create a Partial which will have the following content:

_Refeicoes.cshtml

    @model NutriSport.Models.RefRefeicao

    @using (Html.BeginCollectionItem("RefRefeicao")) @* O nome precisa ser o mesmo que você definiu a List<RefRefeicao> no ViewModel *@
    {
        <tr class="success">
            <td>
                @Html.TextBoxFor(model => model.RefTipo), null, new { @class = "form-control" })
            </td>

            <td>
                @Html.TextBoxFor(model => model.RefHorarioLocal, null, new { @class = "form-control" })
            </td>

            <td>
                @Html.TextAreaFor(model => model.RefAlimentosQuantidades, i), null,  new { @class = "form-control" })
            </td>
        </tr> 
    }

The component creates the index for you. You don’t have to do anything else.

If everything was done right, by sending your <form> to the Controller who receives AnamineseViewModel for [HttpPost], you will see that RefRefeicao will be appropriately filled.

  • Putting the <input type="hidden" name="RefRefeicao.index" id="RefRefeicao_index" value="@i.ToString()" /> didn’t work. =(

  • What didn’t work? Tag generation? Form submission? Filling out? See this answer, where it is done manual.

  • Filling in the data... The fields remain blank.. The data is sent, but is still blank. =(

  • Ah, good. Naturally they will go blank, because their approach, using @Html.TextBox, does not define values (the value is the second parameter, in which you are passing null for everyone). By trial and error will not work even.

  • How could I solve this situation? Putting the inputs in pure html instead of using Razor? Or da para resolver using Razor?

  • Well, if I mentioned null, that it represents the value, replacing the null by the value will naturally appear the input filled.

  • But like, being a list, how could I replace that value?

  • Element by element, young. Inside the foreach, exactly as you were doing to write each other’s name input.

  • I tried and it didn’t work.. I put as string: @Html.TextBox(string.Format("RefRefeicao[{0}].RefTipo", i), string.Format("RefRefeicao[{0}].RefTipo", i), new { @class = "form-control" }) and it doesn’t work.. = (.. What is shown is Refresh[{n}]. Type

Show 4 more comments

Browser other questions tagged

You are not signed in. Login or sign up in order to post.