asp.net mvc (分割されたビューモデル、単一モデル) でのマルチステップ登録プロセスの問題に関するDarin の投稿に従い ました。
これは非常に洗練されたソリューションですが、個々のステップビューモデルにデータを入力する方法がわかりません。私は、住所の選択から始まり、配送オプション、支払い情報の順に、アマゾンのチェックアウトステップシステムをエミュレートしようとしています。
私の最初のビューモデルでは、現在ログインしているユーザーのアドレスのリストが必要で、データベースをポーリングして画面に表示します
私の頭の中では、これが理にかなっているビューモデルです。
[Serializable]
public class ShippingAddressViewModel : IStepViewModel
{
public List<AddressViewModel> Addresses { get; set; }
[Required(ErrorMessage="You must select a shipping address")]
public Int32? SelectedAddressId { get; set; }
#region IStepViewModel Members
private const Int32 stepNumber = 1;
public int GetStepNumber()
{
return stepNumber;
}
#endregion
}
ただし、コントローラーからアドレスを入力する良い方法はないようです。
public class WizardController : Controller
{
public ActionResult Index()
{
var wizard = new WizardViewModel();
wizard.Initialize();
return View(wizard);
}
[HttpPost]
public ActionResult Index(
[Deserialize] WizardViewModel wizard,
IStepViewModel step)
{
wizard.Steps[wizard.CurrentStepIndex] = step;
if (ModelState.IsValid)
{
if (!string.IsNullOrEmpty(Request["next"]))
{
wizard.CurrentStepIndex++;
}
else if (!string.IsNullOrEmpty(Request["prev"]))
{
wizard.CurrentStepIndex--;
}
else
{
// TODO: we have finished: all the step partial
// view models have passed validation => map them
// back to the domain model and do some processing with
// the results
return Content("thanks for filling this form", "text/plain");
}
}
else if (!string.IsNullOrEmpty(Request["prev"]))
{
// Even if validation failed we allow the user to
// navigate to previous steps
wizard.CurrentStepIndex--;
}
return View(wizard);
}
}
だから私はアドレスビューモデルのリストを削除しました
[Serializable]
public class ShippingAddressViewModel : IStepViewModel
{
[Required(ErrorMessage="You must select a shipping address")]
public Int32? SelectedAddressId { get; set; }
#region IStepViewModel Members
private const Int32 stepNumber = 1;
public int GetStepNumber()
{
return stepNumber;
}
#endregion
}
これが、ビュー モデル用のカスタム エディター テンプレートを思いついたものです。ユーザー コントローラーからすべてのアドレスの部分ビューを返す Html.RenderAction を呼び出し、Jquery を使用してビュー モデルの必須の SelectedAddressId プロパティの非表示の入力フィールドを設定します。
@model ViewModels.Checkout.ShippingAddressViewModel
<script src="../../Scripts/jquery-1.7.1.js" type="text/javascript"></script>
<script type="text/javascript">
$(document).ready(function () {
//Check to see if the shipping id is already set
var shippingID = $("#SelectedAddressId").val();
if (shippingID != null) {
$("#address-id-" + shippingID.toString()).addClass("selected");
}
$(".address-id-link").click(function () {
var shipAddrId = $(this).attr("data-addressid").valueOf();
$("#SelectedAddressId").val(shipAddrId);
$(this).parent("", $("li")).addClass("selected").siblings().removeClass("selected");
});
});
</script>
<div>
@Html.ValidationMessageFor(m => m.SelectedAddressId)
@Html.HiddenFor(s => s.SelectedAddressId)
<div id="ship-to-container">
@{Html.RenderAction("UserAddresses", "User", null);}
</div>
</div>
そして、ユーザーのコントローラーアクション
[ChildActionOnly]
public ActionResult UserAddresses()
{
var user = db.Users.Include("Addresses").FirstOrDefault(
u => u.UserID == WebSecurity.CurrentUserId);
if (user != null)
{
return PartialView("UserAddressesPartial",
Mapper.Map<List<AddressViewModel>>(user.Addresses));
}
return Content("An error occured");
}
部分的なビュー
@model IEnumerable<AddressViewModel>
<ul>
@foreach (var item in Model)
{
<li id="address-id-@item.AddressID">
@Html.DisplayFor(c => item)
<a class="address-id-link" href="#" data-addressid="@item.AddressID">Ship To this Address
</a></li>
}
</ul>
私の解決策は、私にとっては途方もなく/ずさんなように思えます.これに別のコントローラーからの部分ビューを使用するよりも、ビューモデルを設定するためのより良いより簡潔な方法はありますか?