MVC ASP.NET 2 - 向导通过项目数组

时间:2011-02-22 13:31:50

标签: asp.net-mvc

我接受了一些建议。我是MVC的新手,并且一直在尝试整合一个使用Web服务来获取数据的示例应用程序。数据从Microsoft Navision数据库返回。 我希望将一个屏幕放在一起的是一个向导,它允许用户在项目列表中前后转发并整理所有修改,然后通过公开的Web服务提交回数据库。 我对此的想法是最小化Web服务调用。

我已经关注了Steven Sandersons - Pro ASP.NET MVC 2框架书,他有一个关于向导的部分,并且在各种控制器中对对象进行序列化和反序列化,但在我的情况下,我希望调用相同的控制器,但可能是不同的索引号显示正确的Item记录。 View将具有对List的强类型引用。

请你告诉我这是不是正确的方法,或者是否确实可行。

非常感谢

pf79


更新

感谢您的回答,但我想我找到了答案。我试图保持服务器端,因为我可能在Windows 6移动设备上使用它,而且我之前并没有真正使用过javascript;

使用TempData和Serialization的组合我能够实现这一目标。不确定这是否是“最佳做法”。任何关于如何改进的建议都将不胜感激。

我有一个项目类

public class Item
{
    public int Id { get; set; }
    public string Name { get; set; }
    public decimal Price { get; set; }
}

然后我有一个webservice调用,它从Controller类中的另一个方法返回一个Item列表。

public ActionResult GetDetails()
        {
            //Test filling List. Will be retrieved from Web Service eventually.
            testItemList = new List<Item>();
            testItemList.Add(new Item() { Id = 1, Name = "Bike", Price = new decimal(1000) });
            testItemList.Add(new Item() { Id = 2, Name = "Wheel", Price = new decimal(99.99) });
            testItemList.Add(new Item() { Id = 3, Name = "Saddle", Price = new decimal(49.99) });


            TempData["itemList"] = testItemList;
            return RedirectToAction("Details");
        }

这通过OnResultExecuted

将List和currentIndex放入TempData
protected override void OnResultExecuted(ResultExecutedContext filterContext)
{
    if (filterContext.Result is RedirectToRouteResult)
    {
        TempData["testItemList"] = testItemList;
        TempData["currIndex"] = currentIndex;
    }
}

控制器

[HttpGet]
public ActionResult Details()
{
    testItemList = (IList<Item>)TempData["itemList"];
    currentIndex = (int)TempData["currIndex"];

    return View(testItemList[currentIndex]);
}

[HttpPost]
public ActionResult Details(string next, string back)
{
    var testItem = testItemList[currentIndex];

    if (TryUpdateModel(testItem))
    {
        if (next != null)
        {
            currentIndex = (currentIndex + 1) == testItemList.Count ? currentIndex : currentIndex + 1;
        }

        if (back != null)
        {
            currentIndex = (currentIndex - 1) < 0 ? currentIndex : currentIndex - 1;
        }

        TempData["currIndex"] = currentIndex;
        TempData["itemList"] = testItemList;

        return RedirectToAction("Details"); 
    }

    TempData["currIndex"] = currentIndex;
    TempData["itemList"] = testItemList;

    return View(testItem);
}

然后在View上我正在序列化List和当前索引,以便我可以跟踪它。

protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
    var listSerialized = Request.Form["itemList"];
    if (listSerialized != null)
    {
        testItemList = (IList<Item>)new MvcSerializer().Deserialize(listSerialized);
    }

    var indexSerialized = Request.Form["currIndex"];
    if (indexSerialized != null)
    {
        currentIndex = (int)new MvcSerializer().Deserialize(indexSerialized);
    }
    else
    {
        currentIndex = 0;
    }

}

查看

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<ArrayTest.Models.Item>" %>

<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
    Details
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
    <h2>
        Details</h2>
    <% int Index = (int)TempData["currIndex"]; %>    
    <% var ItemList = (List<ArrayTest.Models.Item>)TempData["itemList"]; %>
    <% using (Html.BeginForm())
       {%>       
    <%: Html.Serialize("itemList",ItemList) %>
    <%: Html.Serialize("currIndex",Index) %>    
    <%: Html.ValidationSummary() %>
    <p>
        <%: Html.EditorFor(t => t.Name) %></p>
    <p>
        <%: Html.EditorFor(t => t.Price) %></p>
    <input type="submit" value="Back" name="back" />
    <input type="submit" value="Next" name="next" />
    <%} %>
</asp:Content>

2 个答案:

答案 0 :(得分:1)

这在很大程度上取决于表单的复杂程度以及是否需要存储临时数据,如果用户需要半填表单并再次返回,可能会保存。

我个人赞成使用各种jquery wizard plugin可用的客户端向导jQuery lightbox plugin虽然不适合向导,但可以用它。这有一些好处:

  • 限制为最后一次通话的通话次数,用户可以根据自己的意愿进行多次修改。因此,就Web服务而言,呼叫是最佳的。
  • 更好地利用服务器端技术,如ASP NET MVC
  • 它可以通过简单的实施方式节省半满的表格。

但它需要非常好的客户端验证才能最大限度地减少因无效日期而导致的往返行程,但现在有数十种工具可供选择,以便进行稳健的客户端验证。

答案 1 :(得分:0)

如果您真的需要服务器端向导,请尝试以下方法:

假设您有一个将在向导完成时填充的模型。

// This is a pseudo-model to show the properties Member has in this example.
// I use entity framework when generating my model objects. How you generate
// your model objects is not important in this example.
public class Member
{
    public string Username { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Email { get; set; }
    public string ReferredBy { get; set; }
    public string Address { get; set; }
    public string City { get; set; }
    public string State { get; set; }
    public string Country { get; set; }
}

我整理了一个名为Member的简单模型,我们的向导最终会填充这个模型。现在我将构建一个在我们的向导页面上使用的视图模型。

public class RegisterViewModel
{
    public Member NewMember { get; set; }
    public int Step { get; set; } //int cannot be null and will be 0 by default.
}

现在我要构建一些动作方法。

// First request will pull up the first view of the wizard with 
// a new RegisterViewModel.
[HttpGet]
public ViewResult RegisterNewMember()
{
    return View("RegisterWizardStep1", new RegisterViewModel());
}

// This will be the post action method. Rather than create a different
// action method for every step of the wizard, our view model will support
// which step we are on and this method will be aware of what to do.
[HttpPost]
public ViewResult RegisterNewMember(RegisterViewModel viewModel)
{
    if (ModelState.IsValid)
    {
        if (viewModel.Step < 2)
            return View(String.Format("RegisterWizardStep{0}", viewModel.Step + 1), viewModel);
        else
        {
            db.Members.AddObject(viewModel.NewMember);
            db.SaveChanges();
            // db is a fictional context. The syntax is that of entity framework.
            // You could substitute this for a LINQ to SQL context or any other
            // data storage component.
            ViewBag.WelcomeMessage = "Thank you for registering. We welcome you!";
            return View("Welcome");
        }
    }
    else
        return View(String.Format("RegisterWizardStep{0}", viewModel.Step));
        // Assuming you have model validation set up, returning the same view with
        // an invalid modelstate will trigger your validation helpers.
}

在这个例子中,我想要向导中的三个步骤。因此,我们将有三种观点。

第一步。

// RegisterWizardStep0
@model AppName.Models.RegisterNewMember
@using AppName.Models;
@{
    Layout = "~/Views/Shared/Master.cshtml";
}

@* I'm not going to worry much about formatting in these views as they are only samples.
   I am also not going to worry about client-side validation before step submission;
   I will let you worry about that. *@

@using(Html.BeginForm())
{
    <text>
    @Html.HiddenFor(x => x.Step)

    @Html.TextBoxFor(x => x.NewMember.Username)
    @Html.ValidationMessageFor(x => x.NewMember.Username)
    <br />
    @Html.TextBoxFor(x => x.NewMember.FirstName)
    @Html.ValidationMessageFor(x => x.NewMember.FirstName)
    <br />
    @Html.TextBoxFor(x => x.NewMember.LastName)
    @Html.ValidationMessageFor(x => x.NewMember.LastName)
    <br />
    <input type="submit" value="Next" />
    </text>
}

现在是第2步。

// RegisterWizardStep1
@model AppName.Models.RegisterNewMember
@using AppName.Models;
@{
    Layout = "~/Views/Shared/Master.cshtml";
}

@using(Html.BeginForm())
{
    <text>
    @Html.HiddenFor(x => x.Step)

    @Html.HiddenFor(x => x.Member.Username)
    @Html.HiddenFor(x => x.NewMember.FirstName)
    @Html.HiddenFor(x => x.NewMember.LastName)

    @Html.TextBoxFor(x => x.NewMember.Email)
    @Html.ValidationMessageFor(x => x.NewMember.Email)
    <br />
    @Html.TextBoxFor(x => x.NewMember.ReferredBy)
    @Html.ValidationMessageFor(x => x.NewMember.ReferredBy)
    <br />
    <input type="submit" value="Next" />
    </text>
}

最后是第3步。

// RegisterWizardStep2
@model AppName.Models.RegisterViewModel
@using AppName.Models;
@{
    Layout = "~/Views/Shared/Master.cshtml";
}

@using(Html.BeginForm())
{
    <text>
    @Html.HiddenFor(x => x.Step)

    @Html.HiddenFor(x => x.NewMember.Username)
    @Html.HiddenFor(x => x.NewMember.FirstName)
    @Html.HiddenFor(x => x.NewMember.LastName)
    @Html.HiddenFor(x => x.NewMember.Email)
    @Html.HiddenFor(x => x.NewMember.ReferredBy)

    @Html.TextBoxFor(x => x.NewMember.Address)
    @Html.ValidationMessageFor(x => x.NewMember.Address)
    <br />
    @Html.TextBoxFor(x => x.NewMember.City)
    @Html.ValidationMessageFor(x => x.NewMember.City)
    <br />
    @Html.TextBoxFor(x => x.NewMember.State)
    @Html.ValidationMessageFor(x => x.NewMember.State)
    <br />
    @Html.TextBoxFor(x => x.NewMember.Country)
    @Html.ValidationMessageFor(x => x.NewMember.Country)
    <br />
    <input type="submit" value="Done" />
    </text>
}

希望这会有所帮助。请注意,我没有在IDE中测试此代码,因为我不在我的开发计算机上。我确定它需要调整,但你明白了。