【问题标题】:Asp.net Mvc3 webgrid and pagingAsp.net Mvc3 webgrid 和分页
【发布时间】:2011-09-14 16:43:34
【问题描述】:

我正在尝试学习 Asp.net mvc。我知道它与形式不同,我可能需要改变我的思维方式。我的问题是关于 webgrid 。当我将 webgrid 添加到我的页面并使用 Post 点击搜索按钮时,它会使用寻呼机等呈现表格。但是寻呼机上的链接没有发布表单,它们只是链接,我丢失了所有表单的数据。

Controller 有两种索引方法,一种用于获取,另一种用于发布。为了让我什么都不做,我只是在这种情况下创建新的视图模型搜索类并将其设置为视图。对于我的发布方法,我抓住我的视图模型进行搜索并将填充的视图模型设置为查看。

问题:webgrid 将分页器呈现为链接,因此它将进入索引以获取但由于它不是发布请求,因此我没有填写任何表单字段,并且我的搜索不会提供完全相同的结果集。

也许示例代码可以更好地解释它。

查看:

<form action="" method="post">

Esas no : @Html.TextBoxFor(x=>x.Name)
Yil : @Html.TextBoxFor(x=>x.Year)

<input type="submit" value="Search" />

<hr />
@ViewBag.Message
<hr />

@{ var grid = new WebGrid(Model.Results,rowsPerPage:5);}

@grid.GetHtml(tableStyle:"table",htmlAttributes:new {id="tbl"} )

</form>

这是我的控制器:搜索发生在 Index Post 方法中,它只有我的视图模型类。

    private ISearchContext _sc;

    public  MyController(ISearchContext sc)
    {
        _dc = dc;
    }

    //
    // GET: /Dava/

    public ActionResult Index()
    {
        var search = new Search();
        ViewBag.Message = "";
        return View(search);
    }

    [HttpPost]
    public ActionResult Index(Search search)
    {

        Search sres = _dc.SearchFromRepository(search);
        ViewBag.Message = String.Format("Count:{0} ",sres.Results.Count);
        return View(sres);
    }

搜索模型类是这样的:

public class Search
{
    public int Year { get; set; }
    public string Name { get; set; }


    public IList<Item> Results { get; set; }

    public Search()
    {
        Results = new List<Item>();
    }
}

【问题讨论】:

    标签: asp.net-mvc-3 webgrid


    【解决方案1】:

    解决此问题的一种方法是使用 javascript 并订阅任何寻呼机链接的点击事件,然后获取所需页面的值,将其注入表单上的隐藏字段并将表单提交给服务器,以便其他两个值也被发送。

    首先在您的Search 视图模型上添加一个Page 可为空的整数属性,并将相应的隐藏字段添加到包含所选页码的表单中:

    @Html.HiddenFor(x => x.Page, new { id = "page" })
    

    那么你只需要一个小的 javascript sn-p 进入页面来订阅页面链接的 .click 事件:

    $(function () {
        $('tfoot a').click(function () {
            // when the user clicks on any of the pager links
            // try to extract the page number from the link and
            // set the value of the hidden field
            var page = this.href.match(/page=([0-9])+/)[1];
            $('#page').val(page);
    
            // submit the form so that the POST action is invoked
            // passing along the search criteria (Name and Year) along
            // with the page hidden field value to the Index action
            $('form').submit();
    
            // cancel the default action of the link which is to simply redirect
            // to the Index action using a GET verb.
            return false;
        });
    });
    

    【讨论】:

    • 你的建议很有效,但我是唯一一个认为这样一个微不足道的任务不应该那么难完成的人。我还需要添加一些代码来进行排序等等......
    • @adt,好吧,如果您是长期的经典 WebForms 开发人员,这是一个完全正常的反应。经典 WebForms 是泄漏的抽象,隐藏了 Web 的实际工作方式(诸如 HTTP 协议、HTML 表单、javascript 之类的东西已经隐藏在某些事件、PostBacks、ViewStates 等之后......)。这就是为什么你可能会觉得它很奇怪,但这是完全正常的:-)
    • @adt,你当然是对的,奇怪的是这种常见的场景并不容易。然而,WebGrid 只是呈现网格的一种可能的解决方案,有其优点和缺点。显然它不支持过滤(既不支持某些集成解决方案,也不支持轻松回发表单数据)。也许这不是您具体案例的正确选择?您可以找到其他解决方案,它们具有各种抽象级别和对“常见”功能(如排序、分页、过滤、分组、层次结构等)的各种支持级别。不幸的是,它们中的大多数都不是免费的......
    • 感谢您的 cmets,我知道我必须改变我的思维方式,但我仍然这么想:“当然,我想要完全控制 html(没有丑陋的视图状态,也没有糟糕的更新面板)并想在我的控制器等上使用 DI,但我真的不想花很多时间网格寻呼和排序,整个团队必须处理很多会减慢开发过程的事情。
    • 请使用局部视图查看我的解决方案。使用我的方法,您不需要 ViewState、TempData、ViewBag、Javascript 或任何那些复杂的方法。
    【解决方案2】:

    这是一个不使用 JavaScript 的解决方法。

    我看到的问题是,分页链接没有收到任何必须保留的路由信息​​,例如搜索过滤器。 IMO这是一个公然的疏忽!在这里稍微多想一下就可以省去很多麻烦!

    这种技术“抛弃”了 WebGrid 的内置分页,并使用 Helper 生成分页链接以及我们想要的宝贵路由数据。

    一旦完成,您只需将 WebGrid 呈现为仅网格,并使用 Helper 来制作分页链接。这里的一个优点是您可以将它们放在顶部和底部,这是我们喜欢做的。

    我尝试使用与 NuGet 放入您的解决方案的 Pager.css 中提供的类似的 CSS。对于你们中的一些人来说,帮助器应该足够完整,但它很容易扩展。

    New New New 我刚刚用 Ajax 版本更新了助手。我对 Razor 助手有点 n00b,所以我不知道如何重新分解它以使用通用模板;请问有人吗?重要的额外细节是传递AjaxOptions 并确保使用POST 作为动词,否则您可能无法使用正确的控制器方法。

    助手(App_Code/LocalHelpers.cshtml):

    @helper DoPager(System.Web.Mvc.HtmlHelper hh, string pageActionName, WebGrid grid, int maxPageLinks, object rvd) {
    <div class="pager">
    <div class="pageof">Page <b>@(grid.PageIndex + 1)</b> of <b>@grid.PageCount</b></div>
    @if (grid.PageCount > 1) {
    <ul>
    <li>
    @{ RouteValueDictionary rvdp1 = new RouteValueDictionary(rvd);
       rvdp1.Add("Page", 1);
    }
    @hh.ActionLink("<<", pageActionName, rvdp1)
    </li>
    @{ int start = Math.Max(0, grid.PageIndex - maxPageLinks / 2); }
    @for (int ix = 0; ix + start < grid.PageCount; ix++) {
        int pageno = start + ix + 1;
        var css = hh.Raw(pageno - 1 == grid.PageIndex ? " class=\"highlighted\"" : "");
        RouteValueDictionary rvdp = new RouteValueDictionary(rvd);
        rvdp.Add("Page", pageno);
    <li@css>
    @hh.ActionLink(pageno.ToString(), pageActionName, rvdp)
    </li>
        if (ix >= maxPageLinks) { break; }
    }
    <li>
    @{ RouteValueDictionary rvdpX = new RouteValueDictionary(rvd);
       rvdpX.Add("Page", grid.PageCount);
    }
    @hh.ActionLink(">>", pageActionName, rvdpX)
    </li>
    </ul>
    }
    </div>
    }
    @helper DoAjaxPager(System.Web.Mvc.AjaxHelper aa, System.Web.Mvc.Ajax.AjaxOptions aopts, System.Web.Mvc.HtmlHelper hh, string pageActionName, WebGrid grid, int maxPageLinks, object rvd) {
    <div class="pager">
    <div class="pageof">Page <b>@(grid.PageIndex + 1)</b> of <b>@grid.PageCount</b></div>
    @if (grid.PageCount > 1) {
    <ul>
    <li>
    @{ RouteValueDictionary rvdp1 = new RouteValueDictionary(rvd);
       rvdp1.Add("Page", 1);
    }
    @aa.ActionLink("<<", pageActionName, rvdp1, aopts)
    </li>
    @{ int start = Math.Max(0, grid.PageIndex - maxPageLinks / 2); }
    @for (int ix = 0; ix + start < grid.PageCount; ix++) {
        int pageno = start + ix + 1;
        var css = hh.Raw(pageno - 1 == grid.PageIndex ? " class=\"highlighted\"" : "");
        RouteValueDictionary rvdp = new RouteValueDictionary(rvd);
        rvdp.Add("Page", pageno);
    <li@css>
    @aa.ActionLink(pageno.ToString(), pageActionName, rvdp, aopts)
    </li>
        if (ix >= maxPageLinks) { break; }
    }
    <li>
    @{ RouteValueDictionary rvdpX = new RouteValueDictionary(rvd);
       rvdpX.Add("Page", grid.PageCount);
    }
    @aa.ActionLink(">>", pageActionName, rvdpX, aopts)
    </li>
    </ul>
    }
    </div>
    }
    

    查看:

    <center>
    @LocalHelpers.DoPager(Html, "Index", grid, 10, new { CurrentFilter = ViewBag.CurrentFilter })
    </center>
    @grid.Table(
        tableStyle: "centerit",
        columns: grid.Columns(
            grid.Column(format: @<span>@Html.ActionLink("Edit", "Edit", new { id = item.ID }) | @Html.ActionLink("Details", "Details", new { id = item.ID }) | @Html.ActionLink("Delete", "Delete", new { id = item.ID })</span>),
                grid.Column("PartNumber", "Part Number"),
                grid.Column("Description", "Description"),
                grid.Column("Regex", "Regex")
                )
            )
    <center>
    @LocalHelpers.DoPager(Html, "Index", grid, 10, new { CurrentFilter = ViewBag.CurrentFilter })
    </center>
    

    在我看来,我正在回收“CurrentFilter”以了解要过滤的内容。这将连接到控制器动作(未图示)。

    【讨论】:

    • 排序呢?是否为排序链接保留了路线信息?
    • 它被设置为传递您选择“重新生成”的任何值,例如排序参数、过滤器参数等,但您必须将它们放在添加网格的位置。如果您查看最后一个参数,那是放置您想要“返回”到您的操作的内容的地方。
    • 感谢火焰老男孩,我什至不这样做我只是想解决这个问题,这就是这个网站的目的!这不是减号,所以有些人觉得它很有用,这也是本网站的重点。
    • 有关此解决方案的更多信息,请参阅(帮助)=> a2z.googlecode.com/svn/trunk/PresentationMVC/App_Code/… 和(查看)=> a2z.googlecode.com/svn/trunk/PresentationMVC/Views/Job/… 感谢@escape-llc
    【解决方案3】:

    好的。我有一个使用 AJAX 和部分视图的更优雅的解决方案,应该一劳永逸地解决这个问题

    这是我的模型:

    public class SearchResultModel
    {
            public string SearchText{ get; set; }
            public List<YourObject> Results { get; set; }
            public int TotalResults { get; set; }
    }
    

    搜索视图的结构如下:

    @model SearchResultModel
    @using (Ajax.BeginForm("SearchAction", "SearchController", new AjaxOptions{UpdateTargetId = "data-grid", HttpMethod="Post"}))
    {
            @Html.TextBoxFor(m => m.SearchText)
            <input class="myButton" type="submit" value="Search" />
    }
    <br />
    <div id="data-grid">
           @Html.Partial("SearchResults", new SearchResultModel())
    </div>
    

    SearchResults 部分视图是:

    @model SearchResultModel
    @{
        if (Model.Results != null && Model.Results.Count > 0)
        {
                var grid = new WebGrid(canPage: true, rowsPerPage: 10, canSort: true, ajaxUpdateContainerId: "grid");
                grid.Bind(Model.Results, rowCount: Model.TotalResults, autoSortAndPage: false);
                grid.Pager(WebGridPagerModes.All);
    
                @grid.GetHtml(htmlAttributes: new { id = "grid" },
                columns: grid.Columns(
                    grid.Column("YourColumn1"),
                    grid.Column("YourColumn2"),
                    grid.Column("YourColumn3")
                ),
                tableStyle: "datatable",
                    rowStyle: "datatable-normal",
                        alternatingRowStyle: "datatable-alt"
                );
        }
        else
        {
        <span>No Results</span>
        }
    }
    

    最后,Controller 是:

    public class SearchController
    {
            public ActionResult SearchAction(SearchResultModel model)
            {
                return RedirectToAction("SearchResults", new { id = model.SearchText });
            }
    
            public ActionResult SearchResults(string id)
            {
                string searchText = id;
                int page = 1;
                if(Request["page"] != null)
                    int.TryParse(Request["page"], out page);
    
                SearchResultModel model = new SearchResultModel();
                //Populate model according to search text and page number
                //........
                //........
                return PartialView(model);
            }
    }
    

    希望这将有助于节省一些时间和焦虑!

    【讨论】:

      【解决方案4】:

      我的回答是让您在 Session 上进行搜索,仅此而已。 该解决方案很好,因为您可以根据实际情况对其进行调整,并且不需要特定的类或 JQuery。

      魔术发生在您的 Index ActionResult(或您的默认 ActionResult,它将以其默认行为呈现网格页面)。

      代码示例:

          [HttpGet]
          public ActionResult Index()//My default action result that will render the grid at its default situation
          {
              SearchViewModel model = new SearchViewModel(); 
      
              if (Request.IsAjaxRequest()) //First trick is here, this verification will tell you that someone sorted or paged the grid.
              {
                  if (Session["SearchViewModel"] != null) //If session is not empty, you will get the last filtred values from it.
                      model = (SearchViewModel)Session["SearchViewModel"];
              }
              else // If it is not an AjaxRequest, you have to clear your Session, so new requests to Index with default behavior won't display filtred values.
              {
                  Session["SearchViewModel"] = null;
              }
      
              model.GridResult = ExecuteFilter(model); // OPITIONAL! This code dependes on how is your real world situation. Just remember that you need to return a default behavior grid if the request was not called by the WebGrid, or return filtred results if WebGrid requested.
              return View(model);
          }
      

      因此,这将是您的默认 ActionResult。它将验证请求是否被WebGrid分页或排序事件调用,以决定返回过滤结果还是正常行为结果。

      下一步是搜索POST ActionResult:

          [HttpPost]
          public ActionResult Index(SearchViewModel pesquisa) // IMPORTANT!! It is necessary to be the SAME NAME of your GET ActionResult. The reason for that I know, but won't discuss here because it goes out of the question.
          {
              SearchViewModel model = new SearchViewModel();
              model.GridResult = ExecuteFilter(pesquisa); // Execute your filter
              Session["SearchViewModel"] = model; //Save your filter parameters on Session.
              return View("Index", model);
          }
      

      就是这样。 Index.cshtml 没有任何技巧。只是一个 SearchForm 到 ActionResult 索引,将我的 SearchViewModel 作为参数传递。

      为什么这个解决方案有效?

      好吧,当你点击排序或分页时,WebGrid 会执行一个类似这样的 JavaScript:

      $('#yourGrid').load('it pass the url used to display your current Page, and some paging or sorting parameters, but those are used by the WebGrid')
      

      由于它使用 .load() 方法,因此请求将是 GET,并且会命中您的 Index GET ActionResult。但这是一个 AJAX 调用,所以我们的魔术将使用您保存在 Session 上的参数再次执行过滤器。

      我提醒的唯一细节是关于您的默认网格行为。 GET Index ActionResult 必须永远返回一个有效的网格结果,不管它是否在 Session 上有过滤器。

      【讨论】:

      • 如果更合适,您可以将 Session 更改为 Cache。
      猜你喜欢
      • 2011-10-05
      • 1970-01-01
      • 2013-10-09
      • 2012-03-03
      • 2012-02-15
      • 2013-05-09
      • 2012-02-18
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多