3

私はこの問題について数日間頭を悩ませてきましたが、理解できません。ユーザーが複雑な階層オブジェクトを追加および削除できるビューを作成する必要があります。偽の例:

モデル

public class ParentModel
{
    public int ParentId { get; set; }
    public string ParentText { get; set; }
    public IList<ChildModel> Children { get; set; } 
}
public class ChildModel
{
    public int ChildId { get; set; }
    public string ChildText { get; set; }
    public IList<GrandChildModel> GrandChildren { get; set; }
}
public class GrandChildModel
{
    public int GrandChildId { get; set; }
    public string GrandChildText { get; set; }
}

ここで、a とその子プロパティを設定するアクションがあると仮定するParentModelと、単純なビューは次のようになります。

@model EditorTemplateCollectiosns.Models.ParentModel
@using (@Html.BeginForm())
{
    <div>
        @Html.TextBoxFor(m => m.ParentId)
        @Html.TextBoxFor(m => m.ParentText)<br/>
        Children:<br/>
        @for (var i = 0; i < Model.Children.Count; i++)
        {
            @Html.TextBoxFor(m => Model.Children[i].ChildId)
            @Html.TextBoxFor(m => Model.Children[i].ChildText)
            <div>
                Grandchildren:<br/>
                <table id="grandChildren">
                    <tbody>
                        @for (var j = 0; j < Model.Children[i].GrandChildren.Count; j++)
                        {
                            <tr>
                                <td>@Html.TextBoxFor(m => @Model.Children[i].GrandChildren[j].GrandChildId)</td>
                                <td>@Html.TextBoxFor(m => @Model.Children[i].GrandChildren[j].GrandChildText)</td>
                            </tr>
                        }
                    </tbody>
                </table>
            </div>
        }
    </div>
    <button type="button" id="addGrandchild">Add Grandchild</button>
    <input type="submit" value="submit"/>
}
<script type="text/javascript">
    $(document).ready(function() {
        $("#addGrandchild").click(function() {
            $("#grandChildren tbody:last").append('<tr><td><input type="text" name="grandChildId" /></td><td><input type="text" name="grandChildText" /></td></tr>');
        });
    });
</script>

孫を追加してフォームを送信しない限り、このビューは正常に機能します。その場合、のリストにバインドされませんGrandChildModel。POSTのフィドル:

孫追加後の投稿

そこで、動作するかどうかを確認するために、 のエディター テンプレートを使用してみGrandChildModelました。これは単にオブジェクトを表示するのにはうまく機能しますが、リストに孫を追加してエディター テンプレートに表示する方法がわかりません。また、呼び出しでロードされた部分ビューをネストしようとし$.ajaxましたが、それは論理的な悪夢に変わりました。

実際のアプリケーションでは、階層はこの例よりも深く、連続する子オブジェクトごとにこの追加/削除機能が必要です。ユーザーがオブジェクトを追加または削除するたびにフォームを完全に投稿しなくても、これは可能ですか? IList上記の例のアプローチでは、新しく追加された孫をon POST にバインドすることは可能ですか? または、クライアント側にオブジェクトを追加し、それをエディター テンプレートに表示して、POST にバインドする方法はありますか? IListどちらのオプションも適切なアプローチではない場合、何が適切でしょうか?

更新
私はうまくいくかもしれないハックを見つけましたが、これを行うより簡単な方法があるかどうか知りたいです. 上記のビュー コードを次のように変更しました。

@model EditorTemplateCollectiosns.Models.ParentModel
@using (@Html.BeginForm())
{
    <div>
        @Html.TextBoxFor(m => m.ParentId)
        @Html.TextBoxFor(m => m.ParentText)<br/>
        Children:<br/>
        <button type="button" id="addChild" onclick="addChildRow()">Add Child</button>
        <table id="children">
            <tbody class="childTableBody">
                @for (var i = 0; i < Model.Children.Count; i++)
                {
                    <tr>
                        <td colspan="3">Child @i</td>
                    </tr>
                    <tr class="childInfoRow">
                        <td>
                            @Html.TextBoxFor(m => Model.Children[i].ChildId)
                        </td>
                        <td>
                            @Html.TextBoxFor(m => Model.Children[i].ChildText)                            
                        </td>
                        <td>
                            <button type="button" id="removeChild@(i)" onclick="removeChildRow(this)">Delete</button>
                        </td>
                    </tr>
                    <tr>
                        <td colspan="3">
                            Grandchildren:<br/>
                            <button type="button" id="addGrandchild@(i)" onclick="addGrandchildRow(this)">Add Grandchild</button>
                            <table id="grandChildren@(i)">
                                <tbody>
                                    @for (var j = 0; j < Model.Children[i].GrandChildren.Count; j++)
                                    {
                                        <tr>
                                            <td>@Html.TextBoxFor(m => @Model.Children[i].GrandChildren[j].GrandChildId)</td>
                                            <td>@Html.TextBoxFor(m => @Model.Children[i].GrandChildren[j].GrandChildText)</td>
                                            <td><button type="button" id="removeGrandchild@(i)_@(j)" onclick="removeGrandchildRow(this)">Delete</button></td>
                                        </tr>
                                    }
                                </tbody>
                            </table>
                        </td>
                    </tr>
                }
            </tbody>
        </table>
    </div>
    <input type="submit" value="submit"/>
}

次に、次のjavascriptを追加しました。各追加/削除後にハンドラーを再バインドするため、クラスとjQueryクリックイベントハンドラーを使用できません。正しく機能していないようです:

<script type="text/javascript">
    function addChildRow() {
        var newRowIndex = $("#children tr.childInfoRow").length;
        var newRow = '<tr><td colspan="3">Child ' + newRowIndex + '</td></tr><tr class="childInfoRow"><td><input type="text" name="Children[' + newRowIndex + '].ChildId" /></td>' +
            '<td><input type="text" name="Children[' + newRowIndex + '].ChildText" /></td>' +
            '<td><button type="button" id="removeChild' + newRowIndex + '" onclick="removeChildRow(this)">Delete</button></td></tr>' +
            '<td colspan="3">Grandchildren: <br/><button type="button" id="addGrandchild' + newRowIndex + '" onclick="addGrandchildRow(this)">Add Grandchild</button>' +
            '<table id="grandChildren' + newRowIndex + '"><tbody></tbody></table></td></tr>';
        $("#children tbody.childTableBody:last").append(newRow);
    }
    function addGrandchildRow(elem) {
        var childIndex = $(elem).attr('id').replace('addGrandchild', '');
        var newRowIndex = $("#grandChildren" + childIndex + " tr").length;
        var newRow = '<tr><td><input type="text" name="Children[' + childIndex + '].GrandChildren[' + newRowIndex + '].GrandChildId" /></td>' +
            '<td><input type="text" name="Children[' + childIndex + '].GrandChildren[' + newRowIndex + '].GrandChildText" /></td>' +
            '<td><button type="button" id="removeGrandchild' + childIndex + '_' + newRowIndex + '" onclick="removeGrandchildRow(this)">Delete</button></td></tr>';
        $("#grandChildren" + childIndex + " tbody:last").append(newRow);
    }
    function removeChildRow(elem) {
        $(elem).closest('tr').prev().remove();
        $(elem).closest('tr').next().remove();
        $(elem).closest('tr').remove();
    }
    function removeGrandchildRow(elem) {
        $(elem).closest('tr').remove();
    }
</script>

これまでのところ、これは機能しており、配列の値は正しくバインドされているようです。ただし、どちらのテーブルにも行を追加するために必要な膨大な量のデータはあまり好きではありません。誰かがより良いアイデアを持っていますか?

4

2 に答える 2