0

我在 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>

我的解决方案对我来说似乎超级偏僻/草率,有没有比使用来自不同控制器的部分视图更好的更简洁的方法来填充视图模型?

4

1 回答 1

1

使用这样的子操作来填充用户的地址并没有错。事实上,我认为这实际上是最佳方法。您已经完全分离了关注点和单一职责。仅仅因为某事需要更多的“部分”(额外的行动、观点等)并不会使它变得草率或错误。

处理此问题的唯一其他方法是依赖注入。也就是说,您ShippingAddressViewModel需要当前登录用户的依赖关系,以便它可以从其构造函数中填充地址列表。但是,由于ShippingAddressViewModel未在您的视图中公开,因此您必须传递依赖关系,Wizard这有点代码味道。Wizard不依赖于用户,但由于您的视图模型在其中抽象出来,它会强制依赖它。

总而言之,虽然有一种方法可以在没有子动作和局部视图的情况下做到这一点,但实际上它会比使用它们更糟糕和草率。

于 2013-04-12T21:10:40.670 回答