How can I pass some objects in ViewBag to the Action? - Preserve search, sort and paging options

旧街凉风 提交于 2019-12-13 16:01:12

问题


I have a view which I show result of a search and sort in a paged list. I add column headers as links to be enable the user to sort based on a column this way:

<tr>
    <th>           
        @Html.ActionLink("Reference No", "Index", 
          new { sortOrder = ViewBag.RefNoSortParm, currentFilter = ViewBag.CurrentFilter })
    </th>
    <th>
        @Html.ActionLink("Category", "Index", 
          new { sortOrder = ViewBag.CatSortParm, currentFilter = ViewBag.CurrentFilter })
    </th>
    ... Some other columns
</tr>

Also I added a pager this way:

Page @(Model.PageCount < Model.PageNumber ? 0 : Model.PageNumber) of @Model.PageCount

@Html.PagedListPager(Model, page => Url.Action("Index",
    new { page, sortOrder = ViewBag.CurrentSort, currentFilter = ViewBag.CurrentFilter }))

And the action which I use looks like this:

private Registry_fssEntities db = new Registry_fssEntities();
public ActionResult Index(string sortOrder, SearchTransacModel searchModel, SearchTransacModel currentFilter, int? page)
{            
    var model = from c in db.TRANSACs
                select c;
    ViewBag.CurrentSort = sortOrder;
    ViewBag.DispDateSortParm = String.IsNullOrEmpty(sortOrder) ? "DispDate" : "";
    ViewBag.RefNoSortParm = sortOrder == "RefNo" ? "RefNo_desc" : "RefNo";
    ViewBag.CatSortParm = sortOrder == "Cat" ? "Cat_desc" : "Cat";   
    if (searchModel.DATEDISPFROM != null || searchModel.DATEDISPTO != null || searchModel.DATEDISPFROM != null || searchModel.OFFICERNAME != null || searchModel.APPNAME != null || searchModel.REFNO != null || searchModel.PROCNAME != null)
    {
        page = 1;
    }
    else
    {
        searchModel = currentFilter;
    }
    if (searchModel != null)
    {
        if (!String.IsNullOrEmpty(searchModel.DATEDISPFROM))
        {                    
            ViewBag.DispFrom = searchModel.DATEDISPFROM.ToString();
        }
        else
        {
            searchModel.DATEDISPFROM = "01/01/" + DateTime.Today.Year.ToString();
            ViewBag.DispFrom = "01/01/" + DateTime.Today.Year.ToString();
        }
        if (!String.IsNullOrEmpty(searchModel.DATEDISPTO))
        {                   
            ViewBag.DispTo = searchModel.DATEDISPTO.ToString();
        }
        else
        {
            searchModel.DATEDISPTO = "31/12/" + DateTime.Today.Year.ToString();
            ViewBag.DispTo = "31/12/" + DateTime.Today.Year.ToString();
        }
    }
    if (searchModel != null)
    {
        var tRANSACs = new TransacBusinessLogic();
        model = tRANSACs.GetTransacs(searchModel);

        ViewBag.currentFilter = searchModel;
    }
    List<TransacViewModel> TransactionList = new List<TransacViewModel>();
    foreach (var item in model)
    {
        TransactionList.Add(new TransacViewModel {
                TRANSID = item.TRANSID,
                REFNO = item?.REFNO,
                PROCESS = item?.PROCESS,
                //CATEGORY = item.PROCESS.CATEGORY,
                DOCTYPE = item?.DOCTYPE,
                DATEDEL = returnasdate(item.DATEDEL),
                DATEDISP = returnasdate(item.DATEDISP),
                APPNAME = item?.APPNAME,
                OFFICER = item?.OFFICER,
                DATEREG = item.DATEREG
            });
    }
    switch (sortOrder)
    {
        case "DispDate":
            TransactionList = TransactionList.OrderBy(x => x.DATEDISP).ToList();
            break;
        case "RefNo":
            TransactionList = TransactionList.OrderBy(t => t.REFNO).ToList();
            break;
        case "RefNo_desc":
            TransactionList = TransactionList.OrderByDescending(t => t.PROCESS.CATEGORY.DETAIL).ToList();
            break;
        case "Cat":
            TransactionList = TransactionList.OrderBy(t => t.PROCESS.CATEGORY.DETAIL).ToList();
            break;
        case "Cat_desc":
            TransactionList = TransactionList.OrderByDescending(t => t.REFNO).ToList();
            break;
        default:
            TransactionList = TransactionList.OrderByDescending(t => t.DATEDISP).ToList();
            break;
    }
    int pageSize = 6;
    int pageNumber = (page ?? 1);            
    return View(TransactionList.ToPagedList(pageNumber, pageSize));
}

SearchTransacModel is a model used to contain my search parameters as I pass them to the controller. It works fine when I submit the search form via the submit button, and I am sending this search criteria back to the view using a ViewBag like this: ViewBag.currentFilter = searchModel;

But when I click any of the sort action links I lose the search parameters. I.e currentFilter is null when I get to the controller.

I am passing currentFilter as query string and assigning the search model to it like in this case:

 @Html.ActionLink("Reference No", "Index", new { sortOrder = ViewBag.RefNoSortParm, currentFilter = ViewBag.CurrentFilter })

Can anyone help please? I am new at MVC btw.


回答1:


You can use such action:

public ActionResult Index(SearchModel searchModel, string sortColumn, string sortOrder)
{
    ViewBag.SearchModel = searchModel;
    ViewBag.SortColumn= sortColumn;
    ViewBag.SortOrder = sortOrder;
    var business = new BusinessLogic();
    var model = business.Search(searchModel, sortColumn, sortOrder);
    return View(model);
}

You can simply mix some route values this way. This way, both search options and sort options will be preserved between requests:

@{
    var routeValues = new RouteValueDictionary(ViewBag.SearchModel ?? new { });
    routeValues["sortColumn"] = ViewBag.SortColumn;
    routeValues["sortOrder"] = ViewBag.SortOrder;
}
@Html.ActionLink("Link Text", "Action", routeValues);

Note:

  • Based on the idea in this answer you can simply create a method which mixes even 2 objects to create route values for you.

  • You also can encapsulate sort options in a SortOption class and put it in ViewBag and use it in model binding.

  • System.Linq.Dynamic will help you to have a more dynamic and clean code for sort.

  • To learn more about benefits of such SearchModel and BusinessLogic technique take a look at Filter/Search using Multiple Fields - ASP.NET MVC

  • A common scenario for this requirement is for example, for page links of a pager or column links of a grid which is showing the result of a search and sort. If you are using this technique on a grid, paging links should preserve search options and sort options between requests. But for generating column header links, for each column you should use different suitable sort order and sort column; also for the column which its name equals to current sort column, the sort order should be inverse of current sort order. Also column headers should also preserve page index.




回答2:


You can't pass a value from your viewbag back to the controller.

Instead you should use model binding in your view to pass the information to your post method.




回答3:


You can pass the filter values in querystring after reading from viewbag. Assuming your SearchTransacModel class has the DATEDISPFROM and OFFICERNAME properties,

public class SearchTransacModel
{
     public string DATEDISPFROM { set; get; }
     public string OFFICERNAME { set; get; }
}  

You can send the values as route values where the key will be the property name (of SearchTransacModel class) and value will be the value you have.

@{
    var filter = ViewBag.CurrentFilter as SearchTransacModel;
}
@Html.ActionLink("Reference No", "Index", new { sortOrder = ViewBag.RefNoSortParm, 
        DATEDISPFROM = filter!=null? filter.DATEDISPFROM:"", 
        OFFICERNAME = filter != null ? filter.OFFICERNAME : "" })

Model binder will be able to map the querystring values to your currentFilter object.

I suggest you not use 2 parameters with same type (currentFilter & searchModel).




回答4:


While ViewBag can be used to achieve what you are looking for, it is still not designed for that purpose.

Instead, you should create a Model and use it to communicate with your controller and view.

Change and align your return model to be a little bigger.

public TransactionsViewModel
{
    public SearchTransacModel SearchModel { get; set; }
    public List<TransacViewModel> TransactionList { get; set; }
}

Now you can drop the usage of ViewBag.currentFilter and use only this model.



来源:https://stackoverflow.com/questions/39212140/how-can-i-pass-some-objects-in-viewbag-to-the-action-preserve-search-sort-an

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!