在上一篇介紹完了Service如何自動處理搜索和Controller如何呼叫這個Service之後,接下來就要看view將會如何呼叫,並且透過一些Helper方便產生有正確RouteValue的連接。
功能描述
先看一下這一篇會介紹的內容,再來才詳細的描述。在這一篇會有以下的內容:
- 專門顯示SearchViewModelBase的EditorTemplate - 方便有一個統一的搜索顯示畫面
- Helper方便產生SearchViewModel看的懂的RouteValue
- PagedList.Mvc 顯示搜索結果的分頁清單
顯示SearchViewModelBase的EditorTemplate
通常來說,一個網站裡面的搜索風格會一致。也就是說,基本的內容都一樣,但是不同之處在於實際的搜索欄位,才會有整個網站的一致性。
透過搭配EditorTemplate,這個能夠很容易的做到。
首先,會在Views -> Shared ->EditorTemplates ->SearchFormViewModelBase.cshtml
建立出搜索ViewModel的一個Template,裡面的內容如下:
@model SearchFormViewModelBase
@{
@*用Reflection取得目前這個ViewModel屬於搜索條件的Properties Name -
注意,這裡並不包含BaseViewModel的Property,而是只有繼承下來的Property
*@
var searchablePropertiesName = ReflectionHelper.
GetPropertiesOfCurrentType(Model.GetType()).
Select(x => x.Name);
var properties = ViewData.ModelMetadata.Properties.
Where(x => searchablePropertiesName.Contains(x.PropertyName));
}
@if(properties.Count() > 0)
{
<h3 class="box-title">搜索條件</h3>
@using (Html.BeginForm())
{
<div class="form-horizontal">
@foreach (var prop in properties)
{
@*因為搜索的ViewModel屬於Index ViewModel的一個Property,因此需要加上Prefix避免ModelBinding不到
- Prefix就是SearchViewModel在Index裡面的名字 - SearchForm*@
@*假設這個搜索欄位是一個下拉選單,在ViewData應該會有資料,要不然就是一般的輸入框*@
if (ViewData.ContainsKey(PagingHelper.PropertyNamePrefix + prop.PropertyName))
{
<div class="form-group">
@Html.Label(prop.PropertyName,
htmlAttributes: new { @class = "control-label col-md-4" })
<div class="col-md-8">
@Html.DropDownList(prop.PropertyName,
ViewData[PagingHelper.PropertyNamePrefix
+ prop.PropertyName] as IEnumerable<SelectListItem>,
"", htmlAttributes: new { @class = "form-control" })
</div>
</div>
}
else
{
<div class="form-group">
@Html.Label(prop.PropertyName,
htmlAttributes: new { @class = "control-label col-md-4" })
<div class="col-md-8">
@Html.Editor(prop.PropertyName,
new { htmlAttributes = new { @class = "form-control" } })
</div>
</div>
}
}
@Html.HiddenFor(x => x.Page)
@Html.HiddenFor(x => x.PageSize)
<div class="box-footer">
<input type="submit" value="搜索" class="btn btn-default" />
</div>
</div>
}
}
假設搜索欄位有什麼特別的實際,可以直接在這裡面修改,最後,每一頁要呈現搜索條件只需要在Index.cshtml寫:
@Html.EditorFor(x => x.SearchForm)
把SearchViewModel內容轉成RouteValueDictionary的Helper
有時候如果需要產生目前的搜索內容的值用作於產生鏈接的時候,一個Helper產生RouteValueDictionary是非常重要的。
可以在定義一個如下的Helper:
/// <summary>
/// 依照SearchModel的值,產生出RouteValueDictionary
/// </summary>
/// <param name="model">SearchModel的Instance</param>
/// <param name="rvd">需要增加到RouteValueDictionary的額外值</param>
/// <returns>返回產生的RouteValueDictionary</returns>
public static RouteValueDictionary GenRVDForSearchModel(object model,
RouteValueDictionary rvd = null)
{
if (rvd == null)
{
rvd = new RouteValueDictionary();
}
var properties = ReflectionHelper.GetPropertiesOfCurrentType(model.GetType());
for (int i = 0; i < properties.Length; i++)
{
var value = properties[i].GetValue(model);
if (string.IsNullOrEmpty(value.NonNullString()) == false)
{
rvd.Add(PropertyNamePrefix + properties[i].Name, value);
}
}
return rvd;
}
基本上就是把Model的內容產生對應的RouteValueDictionary。如果還不清楚這個方法的作用,接下來介紹建立分頁鏈接的時候就會清楚的看到如何使用這個方法。 為搜索結果產生分頁
PagedList.Mvc
裡面有一個Helper可以方便產生分頁:
@Html.PagedListPager(Model.Result, page =>
{
return Url.Action("Index", new {SearchForm.page = page});
});
基本上這個分頁的Helper會幫忙處理一些基本的Layout,我們需要做的就是告訴他每一個分頁連接如何產生即可。
不過這邊要注意到,上面的範例是當搜索條件不存在的情況下,這個連接是對的,如果在搜索條件有作用的情況下,每一個分頁的連接應該要包括目前的搜索條件才對。
因此,這個時候上一篇提到的Helper就有幫助,不過因為每一次建立的分頁會把要建立的分頁頁數帶進來,因此,需要建立新的Helper,來包住上面介紹產生RouteValueDictionary的Helper:
/// <summary>
/// 依照Search Form來產生 Route Value Dictionary
/// </summary>
/// <param name="model">Search Form的ViewModel</param>
/// <param name="page">那一頁</param>
/// <param name="pageSize">頁數</param>
/// <returns>
/// 返回產生的RouteValueDictionary
/// </returns>
public static RouteValueDictionary GenRVDBaseOnSearchFormModel(SearchFormViewModelBase model,
int? page = null, int? pageSize = null)
{
RouteValueDictionary rvd = new RouteValueDictionary();
rvd.Add(PropertyNamePrefix + "Page", page ?? model.Page);
rvd.Add(PropertyNamePrefix + "PageSize", pageSize ?? model.PageSize);
return GenRVDForSearchModel(model, rvd);
}
有了上面的Helper之後,我們產生分頁的方法就能夠變成:
@Html.PagedListPager(Model.Result, page =>
{
return Url.Action("Index",
PagingHelper.GenRVDBaseOnSearchFormModel(Model.SearchForm, page));
});
結語
到目前為止,在框架自動處理搜索的部分從Controller,到Service層到View裡面的使用都介紹了。
相信了解之後,對於要做基本的搜索不會有太大問題。
不過,在實務上面,Service層的自動搜索在使用上面還是會有些問題,最大的問題是,目前搜索只支援完全符合。但是,這樣的搜索一點都不好用。
因此,在下一篇,將會介紹如何把自動處理搜索的部分在進化一步。