Рекомендации MVC с привязкой пользовательской модели для определенного сценария - PullRequest
0 голосов
/ 06 января 2012

Сейчас я работаю над приложением, которое копирует электронную таблицу, например таблицу. Я делал все это с привязкой модели ASP.NET и сочетанием ajax и формы POST. Я столкнулся с некоторыми проблемами, в которых, мне кажется, мне было бы лучше создать связующее для пользовательской модели.

Вот мое заявление:

model binding sample

У нас есть этапы проекта, а затем набор столбцов, соответствующих каждому месяцу с прогнозируемыми денежными потоками.

Справа от этого изображения есть кнопка, которая позволяет добавить дополнительный столбец месяца, и вы можете добавить этап, используя кнопку добавления этапа. Эти два сделаны с помощью AJAX, и затем, когда вы отправляете форму, я хочу, чтобы она сохраняла все изменения за один раз через POST.

Мне удалось маневрировать, используя HTML-помощники, просматривать модели и привязку моделей по умолчанию, чтобы заставить его работать с добавлением столбцов:

В моем основном виде у меня есть это для генерации строк / столбцов в TBODY

        <tbody>
            @for (int stageIndex = 0; stageIndex < Model.Stages.Count; stageIndex++)
            {
                @Html.EditorFor(x => x.Stages[stageIndex])
            }
            </tbody>
        <tfoot>

У меня есть частичное представление для CashflowStageViewModel, которое в настоящее время выглядит следующим образом:

@model BWInvoicing.Ui.Models.CashflowStageViewModel
<tr class="stageRow">
    @Html.HiddenFor(x => x.Stage)
    <td class="stage">
        <div class="stageWrapper">
            <div>
                @Model.Stage
            </div>
            <div class="stageOptions">
                <img class="deleteRowButton"  src="../../../Content/Images/delete.png" id=@Model.Stage />
            </div>
        </div>
    </td>
    <td>
       @Html.TextBoxFor(x => x.Amount, new { @Class = "totalFee emTotal", CashflowStage = Model.Stage })
    </td>
    <td>
        @Html.TextBox("summation", 0, new { @Class = "emTotal summation", CashflowStage = Model.Stage, @readonly = "true" })
    </td>
    @for (int i = 0; i < Model.Months.Count; i++)
    {
        <td>
            @Html.HiddenFor(x => Model.Months[i].Date)
            @Html.TextBoxFor(x => Model.Months[i].Amount, new { @Class = "prediction", CashflowStage = Model.Stage })
        </td>
    }
    <td class="stageHeadingEnd">
        @Model.Stage
    </td>
</tr>

Привязка модели работает путем создания идентификаторов и имен, которые выглядят следующим образом:

id="Stages_5__Months_4__Amount" name="Stages[5].Months[4].Amount"

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

У меня такие вопросы:

  • Согласны ли вы, что подойдет пользовательское связующее для моделей? или мне не хватает простого трюка
  • Что вы порекомендуете мне сделать в моем пользовательском связывателе модели с точки зрения имен и идентификаторов моих полей и ячеек, чтобы прочитать их в посте http. Есть ли какое-то руководство о том, как вы должны попытаться назвать их? Или я должен просто разделить строки?

Под этим я подразумеваю, что для каждой ячейки должен быть скрытый файл с именем STAGENAME_DATE и значением того, что они вводят в текстовое поле? и тогда я могу сделать так, чтобы связыватель модели разделил эти строки, чтобы разделить имя этапа и дату, проанализировать их и назначить значение для этапа на эту определенную дату? Или есть более подходящий способ сделать это?

Ответы [ 2 ]

3 голосов
/ 06 января 2012

Я бы порекомендовал вам взглянуть на превосходную статью Стивена Сандерсона по этому вопросу. Он использует пользовательский помощник Html.BeginCollectionItem, который заботится о генерации собственных имен полей ввода, чтобы вам не приходилось беспокоиться о привязке. Связыватель модели по умолчанию будет работать из коробки.

0 голосов
/ 09 января 2012

Я закончил тем, что прочитал о том, как вы можете использовать свои собственные произвольные индексы здесь: http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx

Непоследовательные индексы Ну, это все здорово и все, но что происходит если вы не можете гарантировать, что представленные значения сохранят последовательный индекс? Например, предположим, что вы хотите разрешить удаление строк перед отправкой списка книг через JavaScript. Хорошие новости что путем введения дополнительного скрытого ввода, вы можете позволить произвольный индексы. В приведенном ниже примере мы предоставляем скрытый ввод с Индексный суффикс для каждого элемента, который нам нужно привязать к списку. Имя каждый из этих скрытых входов одинаков, так как описано выше, это даст модели связующему хорошую коллекцию индексов, чтобы посмотреть за привязку к списку.

В результате я обновил вывод моей модели представления следующим образом:

@model BWInvoicing.Ui.Models.CashflowStageViewModel
<tr class="stageRow">

    @* Setup custom indexer to allow for add/remove rows *@
    @Html.Hidden("Index", Model.Stage)

    @* Store stage to rebuild view model, use bracket notation to allow default model binder to work correctly *@
    @Html.Hidden("[" + Model.Stage + "].Stage",
                   Model.Stage)
    <td class="stage">
        <div class="stageWrapper">
            <div>
                @Model.Stage
            </div>
            <div class="stageOptions">
                <img class="deleteRowButton"  src="../../../Content/Images/delete.png" id=@Model.Stage />
            </div>
        </div>
    </td>
    <td>
        @Html.TextBox("[" + Model.Stage + "].Amount", Model.Amount,
                    new { @Class = "totalFee emTotal", CashflowStage = Model.Stage })
    </td>
    <td>
        @* Readonly javascript display of summation *@ 
        @Html.TextBox("summation", 0,
                    new
                        {
                            @Class = "emTotal summation",
                            CashflowStage = Model.Stage,
                            @readonly = "true"
                        })
    </td>
     @* Output the month inputs, setup the correct indexes *@ 
    @for (int i = 0; i < Model.Months.Count; i++)
    {
        <td>
            @Html.Hidden("[" + Model.Stage + "].Months[" + i + "].Date",
                   Model.Months[i].Date)
            @Html.TextBox("[" + Model.Stage + "].Months[" + i + "].Amount",
                    Model.Months[i].Amount, new { @Class = "prediction", CashflowStage = Model.Stage })
        </td>
    }
    <td class="stageHeadingEnd">
        @Model.Stage
    </td>
</tr>

Я не очень доволен тем, насколько грязна конкатенация, но она работает довольно хорошо.

...