2014年10月13日 星期一

[iThome 第七屆鐵人賽 21] 搜索頁面 - ViewModel的定義

在上一篇介紹完了搜索功能的概念和思路之後,在這一篇開始要看實作的部份。

通常寫Mvc都是從Model開始,因此這一篇將來看一下搜索功能所會使用到的ViewModel

ViewModel的內容

首先,搜索的ViewModel必然會有兩個Property:

  1. 搜索條件
  2. 搜索結果

因此,我們會先從這兩個部份的Property來看起。

搜索條件的ViewModel

搜索條件會有一定有的欄位和各個domain所需要的欄位,因此會先定義一個Base,好方便之後domain來繼承並且提供其他相關欄位。

一定會有的欄位像是:

  1. 每頁筆數
  2. 目前頁數
  3. 排序欄位
  4. 排序順序

Domain相關的欄位就依照各自的需求,例如假設是一篇文章,可能會有以“標題”做搜索或者以“內文”做搜索。

因此,程式碼會如下:

/// <summary>
/// 搜索 Form 的 ViewModel base。定義搜索必須要有的相關欄位。
/// </summary>
public abstract class SearchFormViewModelBase : ISearchFormViewModelBase
{
/// <summary>
/// 目前頁數的值
/// </summary>
private int page;

/// <summary>
/// 取得或設定目前頁數。最小值是1。
/// </summary>
/// <value>
/// 目前頁數
/// </value>
public virtual int Page
{
get
{
if (this.page < 1)
{
this.page = 1;
}

return this.page;
}

set { this.page = value; }
}

/// <summary>
/// 每頁筆數的值
/// </summary>
private int pageSize;

/// <summary>
/// 取得或設定每頁筆數。最小值是15。
/// </summary>
/// <value>
/// 每頁筆數
/// </value>
public virtual int PageSize
{
get
{
if (this.pageSize < 1)
{
this.pageSize = 15;
}

return this.pageSize;
}

set { this.pageSize = value; }
}

/// <summary>
/// 欄位排序的值
/// </summary>
protected string orderByColumnName;

/// <summary>
/// 取得或設定要依照那個欄位做排序。
/// </summary>
/// <value>
/// 依照那個欄位做排序.
/// </value>
public abstract string OrderByColumnName { get; set; }

/// <summary>
/// 取得或設定排序的方向。
/// </summary>
/// <value>
/// <c>true</c> 表示用 ascending排序; otherwise, <c>false</c>.
/// </value>
public bool IsAscending { get; set; }
}

這個ViewModel實作的Interface就不看了,基本上就是這些Property的定義。


這邊有個地方可以注意到就是排序的欄位。因為我們這個SearchFormViewModelBase沒有形態的概念,而通常來說搜索條件會和某一個DB的Table對應。因此,為了方便之後框架幫忙做搜索,這邊又定義一個有強型別的SearchFormViewModelBase

/// <summary>
/// 搜索 Form 的 ViewModel base。有帶上形態,以第一個欄位做排序
/// </summary>
/// <typeparam name="T">Entity Framework裡面Table Entity</typeparam>
public abstract class SearchFormViewModelBase<T> : SearchFormViewModelBase
{
/// <summary>
/// 取得或設定要依照那個欄位做排序。
/// </summary>
/// <value>
/// 依照那個欄位做排序.
/// </value>
public override string OrderByColumnName
{
get
{
if (string.IsNullOrEmpty(this.orderByColumnName))
{
this.orderByColumnName = typeof(T).GetProperties().First().Name;
}

return this.orderByColumnName;
}

set
{
this.orderByColumnName = value;
}
}
}

可以注意到這個Class的定義是Abstract,表示之後的class應該要依照自己的Domain去做繼承,例如如果今天是Post的搜索ViewModel,希望有一個 “標題”的搜索欄位,和排序要以“建立時間”欄位,那麼ViewModel就會是:
public class SearchFormViewModel : SearchFormViewModelBase<Post>
{
[DisplayName("標題")]
public string Title { get; set; }

public override string OrderByColumnName
{
get
{
return "CreateDateTime";
}
}
}

搜索結果


這個部份就比較簡單,就是某一個和Detail頁面一樣ViewModel不過是被一個IPagedList包住。因此他不會像搜索ViewModel一樣有個base而是直接看定義的 ViewModel然後以泛型的方式傳入。



PagedList.Mvc - 好用的分頁套件



搜索ViewModel的主檔


有了這兩個Property的ViewModel之後,就可以建立搜索的ViewModel的Base:

/// <summary>
/// 搜索頁面的ViewModel需要繼承這一個Base。
/// 方便處理Paging和搜索條件相關。
/// 這個方法就兩個Property,用作於表示搜索的form和搜索結果的result。
/// </summary>
/// <typeparam name="TSearchForm">搜索的form ViewModel type。
/// 必須是繼承<see cref="MvcInfrastructure.Common.Base.SearchFormViewModelBase"/>
/// </typeparam>
/// <typeparam name="TPageResult">搜索的結果ViewModel type</typeparam>
public class SearchViewModelBase<TSearchForm, TPageResult> :
ISearchViewModelBase<TSearchForm, TPageResult>
where TSearchForm : Core.Common.Base.ISearchFormViewModelBase, new()
{
private TSearchForm searchForm;

/// <summary>
/// 取得或設定搜索的Form。如果是null,會實例一個。
/// </summary>
/// <value>
/// 搜索的Form
/// </value>
public TSearchForm SearchForm
{
get
{
if (this.searchForm == null)
{
this.searchForm = new TSearchForm();
}

return this.searchForm;
}

set { this.searchForm = value; }
}

/// <summary>
/// 取得或設定搜索結果的ViewModel。
/// </summary>
/// <value>
/// 搜索結果的ViewModel。用<see cref="PagedList.IPagedList"/>包住,方便做分頁
/// </value>
public IPagedList<TPageResult> Result { get; set; }
}


Post的搜索ViewModel


定義好了搜索的BaseViewModel之後,假設今天是Post頁面要做搜索的ViewModel,就可能會像是:

public class Index : SearchViewModelBase<SearchFormViewModel, SearchResult>
{
}

public class SearchResult : IMapFrom<Post>
{
public int Id { get; set; }
[DisplayName("標題")]
public string Title { get; set; }
[DisplayName("內文")]
public string PostContent { get; set; }

}

public class SearchFormViewModel : SearchFormViewModelBase<Post>
{
[DisplayName("標題")]
public string Title { get; set; }

public override string OrderByColumnName
{
get
{
return "CreateDateTime";
}
}
}

結語


有了搜索的ViewModel之後,在下一篇將會介紹框架的Service怎麼能夠做修改並且讓搜索處理邏輯變的簡單。


沒有留言 :

張貼留言