我在 asp.net mvc (splitted viewmodels, single model) 中关注了 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>
我的解决方案对我来说似乎超级偏僻/草率,有没有比使用来自不同控制器的部分视图更好的更简洁的方法来填充视图模型?