1

私は post-redirect-get 設計パターンについて多少読んだことがありますが、アプリケーションのように見えるように設計された MVC サイトであるため、それが私の目的に合っているかどうかはわかりません。ページに複数のドロップダウンがあります。私のコントローラーでは、以下のようにすべて整数配列にバインドします。

    [HttpPost]
    public ViewResult ResponseForm(PartyInvites.Models.GuestResponse response, int[] SelectedCustomer)
    {

       return View(response); // works but resets all my selected dropdowns
       // return View(); // gives an error that it can't rebind items in view

    }

私の見解:

@foreach (Schedule sched in Model.Schedules)
        {
@Html.DropDownList("MySelectedCustomer", new SelectList(sched.Customers, "Id", "FirstName"), "Select A Customer", new { @class = "SelectedCustomer" })

}

ゲストレスポンス:

public class GuestResponse
    {
        [Required(ErrorMessage = "You must enter your name")]
        public string Name { get; set; }
        public string SomeString = "someString";
        public string Email { get; set; }
        public string Phone { get; set; }
        public bool? WillAttend { get; set; }
        public int SelectedSchedule = 0;
        public int SelectedCustomer = 0;

        public List<Schedule> Schedules
        {
            get
            {
                return new List<Schedule>() { new Schedule() { ScheduleName = "party1", ScheduleId = 1 }, new Schedule() { ScheduleId = 2, ScheduleName = "party2" } };
            }
            set
            {
                Schedules = value;
            }
        }
    }

SelectCustomer プロパティは、GuestResponse クラスのプロパティです。すべてのドロップダウンがバインドされており、いくつか変更すると、int[] SelectedCustomer コレクションに適切にバインドされます。ただし、ビューを元に戻したい (本質的には何もしない) が、複数のドロップダウンがあり、MVC がバインドをモデル化できなかったため、応答が完全にバインドされなかったため、すべてのドロップダウンが元の状態にリセットされます。いわば状態を維持するためにこれを行う最良の方法は何ですか?

4

2 に答える 2

2

これを処理する正しい方法は、ドメイン モデルをビューに渡す代わりに、ビュー モデルを使用することです。

しかし、良い慣行に従いたくない場合は、回避策として次のようなドロップダウンを生成できます。

for (int i = 0; i < Model.Schedules.Count; i++)
{
    @Html.DropDownList(
        "MySelectedCustomer[" + i + "]", 
        new SelectList(
            Model.Schedules[i].Customers, 
            "Id", 
            "FirstName", 
            Request["MySelectedCustomer[" + i + "]"]
        ), 
        "Select A Customer", 
        new { @class = "SelectedCustomer" }
    )
}

正しい方法はint[] SelectedCustomers、ビュー モデルに型のプロパティを設定し、厳密に型指定されたバージョンの DropDownListFor ヘルパーを使用することです。

for (int i = 0; i < Model.Schedules.Count; i++)
{
    @Html.DropDownListFor(
        x => x.SelectedCustomers, 
        Model.Schedules[i].AvailableCustomers, 
        "Select A Customer", 
        new { @class = "SelectedCustomer" }
    )
}

そして、あなたの POST コントローラーアクションは明らかに、パラメーターとして定義したビューモデルを取ります:

[HttpPost]
public ViewResult ResponseForm(GuestResponseViewModel model)
{
    // The model.SelectedCustomers collection will contain the ids of the selected
    // customers in the dropdowns

    return View(model);
}

Redirect-After-Post 設計パターンについて言及したので、これは実際に使用する正しいパターンです。成功した場合は、GET アクションにリダイレクトする必要があります。

[HttpPost]
public ViewResult ResponseForm(GuestResponseViewModel model)
{
    if (!ModelState.IsValid)
    {
        // the model is invalid => redisplay the view so that the user can fix
        // the errors
        return View(model);
    }

    // at this stage the model is valid => you could update your database with the selected
    // values and redirect to some other controller action which in turn will fetch the values
    // from the database and correctly rebind the model
    GuestResponse domainModel = Mapper.Map<GuestResponseViewModel, GuestResponse>(model);
    repository.Update(domainModel);

    return RedirectToAction("Index");
}
于 2013-03-31T19:16:36.600 に答える
0

注:最初に、何もバインドしない理由に対処していますが、それは配列の問題に対処していません。これについては後で説明します。ほとんどの人が MVC で間違っているのは、これらの状況に対処するために MVC の組み込み機能を利用していないことです。彼らは、foreach を実行して手動でレンダリングすることを主張しますが、コレクションの状態を考慮していません。

値がリセットされる理由は、Html.DropDownList()ではなくを使用しHtml.DropDownListFor()ており、投稿されたプロパティ名をモデル プロパティ名とは別の名前に変更しているためです。

これを次のように変更するだけです。

@Html.DropDownList("SelectedCustomer", // note the removal of "My"
    new SelectList(sched.Customers, "Id", "FirstName"), 
    "Select A Customer", new { @class = "SelectedCustomer" })

ただし、厳密に型指定されたバージョンを使用していれば、この問題は発生せず、大きな頭痛の種から解放されます。

@Html.DropDownListFor(x => x.SelectedCustomer, 
    new SelectList(sched.Customers, "Id", "FirstName"), 
    "Select A Customer", new { @class = "SelectedCustomer" })

配列に関しては、スケジュールに EditorTemplate を使用する必要があり、その EditorTemplate では、html を単一のアイテムであるかのように作成するだけです。Editor/DisplayTemplates の素晴らしい点は、コレクションを自動的に処理することです。

Views/Controller フォルダーに EditorTemplates というフォルダーを作成します。そのフォルダーに、Schedule.cshtml という空のファイルを作成します (Schedules がリストまたはスケジュールの配列であると仮定します)。その中に、単一のスケジュールをレンダリングするコードがあります。

編集:

ダリンは良い点を指摘します。モデルに小さな変更を加えて、Schedule と GuestResponse の両方に Selected プロパティを追加すると、Linq を使用して選択したスケジュールを返すことができ、作業が簡素化されます。

EDIT2:

あなたが説明した問題とあなたが示したコードの間にいくつかの矛盾があります。コードは実行可能なモデルを実際に反映していないため、何をしようとしているのかを正確に把握することをお勧めします。

于 2013-03-31T19:24:43.520 に答える