0

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>

私の解決策は、私にとっては途方もなく/ずさんなように思えます.これに別のコントローラーからの部分ビューを使用するよりも、ビューモデルを設定するためのより良いより簡潔な方法はありますか?

4

1 に答える 1

1

このような子アクションを使用してユーザーのアドレスを入力しても問題はありません。実際、これが実際に最適なアプローチだと思います。懸念事項を完全に分離し、単一の責任を果たしています。何かがより多くの「ピース」(追加のアクション、ビューなど)を必要とするからといって、それがずさんであったり、間違っていたりするわけではありません。

これを処理する他の唯一の方法は、依存性注入を使用することです。つまり、ShippingAddressViewModel現在ログインしているユーザーの依存関係が必要になるため、コンストラクターのリストからアドレスのリストを作成できます。ただし、ShippingAddressViewModelビューでは公開されていないため、依存関係を渡す必要がありますが、Wizardこれには少しコードの匂いがあります。Wizardユーザーに依存していませんが、ビューモデルがその内部で抽象化されているため、依存が強制されます。

要するに、子アクションと部分ビューを使用せずにこれを行う方法はあります、実際にはそれらを使用するよりも厄介でずさんです。

于 2013-04-12T21:10:40.670 に答える