KnockoutJS是一個JavaScript實現(xiàn)的MVVM框架。接下來通過本文給大家介紹Bootstrap與KnockoutJs相結合實現(xiàn)分頁效果,對bootstrap knockoutjs相關知識感興趣的朋友一起學習吧
KnockoutJS是一個JavaScript實現(xiàn)的MVVM框架。非常棒。比如列表數(shù)據(jù)項增減后,不需要重新刷新整個控件片段或自己寫JS增刪節(jié)點,只要預先定義模板和符合其語法定義的屬性即可。簡單的說,我們只需要關注數(shù)據(jù)的存取。
一、引言
由于最近公司的系統(tǒng)需要改版,改版的新系統(tǒng)我打算使用KnockoutJs來制作Web前端。在做的過程中,遇到一個問題——如何使用KnockoutJs來完成分頁的功能。在前一篇文章中并沒有介紹使用KnockoutJs來實現(xiàn)分頁,所以在這篇文章中,將補充用KnockoutJs+Bootstrap來實現(xiàn)數(shù)據(jù)的分頁顯示。
二、使用KnockoutJs實現(xiàn)分頁
這里采用了兩種方式來實現(xiàn)分頁,第一種是將所有數(shù)據(jù)加載出來,然后再將所有數(shù)據(jù)分頁顯示;第二種是每次都只加載部分數(shù)據(jù),每次請求都重新加載后面的數(shù)據(jù)。
對于這兩種方式,使用Razor方式實現(xiàn)的分頁一般都會采用第二種方式來實現(xiàn)分頁,但是對于單頁面程序來說,第一種實現(xiàn)方式也有其好處,對于不是非常大量的數(shù)據(jù)完全可以采用第一種實現(xiàn)方式,因為這樣的話,后面的數(shù)據(jù)的加載,用戶體驗非常的流暢。所以這里將分別介紹這兩種實現(xiàn)方式。
2.1 每次加載部分數(shù)據(jù)的實現(xiàn)
這里的后端代碼采用的是前一篇文章的代碼,只是多加了一些示例數(shù)據(jù)而已。具體的后端實現(xiàn)代碼為:
/// <summary>
/// Web API 服務,為Web前端提供數(shù)據(jù)服務
/// </summary>
public class TaskController : ApiController
{
private readonly TaskRepository _taskRepository = TaskRepository.Current;
public IEnumerable<Task> GetAll()
{
return _taskRepository.GetAll().OrderBy(a => a.Id);
}
[Route("api/task/GetByPaged")]
public PagedModel GetAll([FromUri]int pageIndex)
{
const int pageSize = 3;
int totalCount;
var tasks = _taskRepository.GetAll(pageIndex, pageSize, out totalCount).OrderBy(a => a.Id);
var pageData = new PagedModel()
{
PageIndex = pageIndex,
PagedData = tasks.ToList(),
TotalCount = totalCount,
PageCount = (totalCount+ pageSize -1) / pageSize
};
//返回數(shù)據(jù)
return pageData;
}
}
/// <summary>
/// 任務倉儲,封裝了所有關于數(shù)據(jù)庫的操作
/// </summary>
public class TaskRepository
{
#region Static Filed
private static Lazy<TaskRepository> _taskRepository = new Lazy<TaskRepository>(() => new TaskRepository());
public static TaskRepository Current
{
get { return _taskRepository.Value; }
}
#endregion
#region Fields
private readonly List<Task> _tasks = new List<Task>()
{
new Task
{
Id =1,
Name = "創(chuàng)建一個SPA程序",
Description = "SPA(single page web application),SPA的優(yōu)勢就是少量帶寬,平滑體驗",
Owner = "Learning hard",
FinishTime = DateTime.Parse(DateTime.Now.AddDays(1).ToString(CultureInfo.InvariantCulture))
},
new Task
{
Id =2,
Name = "學習KnockoutJs",
Description = "KnockoutJs是一個MVVM類庫,支持雙向綁定",
Owner = "Tommy Li",
FinishTime = DateTime.Parse(DateTime.Now.AddDays(2).ToString(CultureInfo.InvariantCulture))
},
new Task
{
Id =3,
Name = "學習AngularJS",
Description = "AngularJs是MVVM框架,集MVVM和MVC與一體。",
Owner = "李志",
FinishTime = DateTime.Parse(DateTime.Now.AddDays(3).ToString(CultureInfo.InvariantCulture))
},
new Task
{
Id =4,
Name = "學習ASP.NET MVC網(wǎng)站",
Description = "Glimpse是一款.NET下的性能測試工具,支持asp.net 、asp.net mvc, EF等等,優(yōu)勢在于,不需要修改原項目任何代碼,且能輸出代碼執(zhí)行各個環(huán)節(jié)的執(zhí)行時間",
Owner = "Tonny Li",
FinishTime = DateTime.Parse(DateTime.Now.AddDays(4).ToString(CultureInfo.InvariantCulture))
},
new Task
{
Id =5,
Name = "測試任務1",
Description = "測試任務1",
Owner = "李志",
FinishTime = DateTime.Parse(DateTime.Now.AddDays(5).ToString(CultureInfo.InvariantCulture))
},
new Task
{
Id =6,
Name = "測試任務2",
Description = "測試任務2",
Owner = "李志",
FinishTime = DateTime.Parse(DateTime.Now.AddDays(6).ToString(CultureInfo.InvariantCulture))
},
new Task
{
Id =7,
Name = "測試任務3",
Description = "測試任務3",
Owner = "李志",
FinishTime = DateTime.Parse(DateTime.Now.AddDays(7).ToString(CultureInfo.InvariantCulture))
},
};
#endregion
#region Public Methods
public IEnumerable<Task> GetAll()
{
return _tasks;
}
public IEnumerable<Task> GetAll(int pageNumber, int pageSize, out int totalCount)
{
var skip = (pageNumber - 1) * pageSize;
var take = pageSize;
totalCount = _tasks.Count;
return _tasks.Skip(skip).Take(take);
}
public Task Get(int id)
{
return _tasks.Find(p => p.Id == id);
}
public Task Add(Task item)
{
if (item == null)
{
throw new ArgumentNullException("item");
}
item.Id = _tasks.Count + 1;
_tasks.Add(item);
return item;
}
public void Remove(int id)
{
_tasks.RemoveAll(p => p.Id == id);
}
public bool Update(Task item)
{
if (item == null)
{
throw new ArgumentNullException("item");
}
var taskItem = Get(item.Id);
if (taskItem == null)
{
return false;
}
_tasks.Remove(taskItem);
_tasks.Add(item);
return true;
}
#endregion
}
Web前端的實現(xiàn)代碼:
@{
ViewBag.Title = "Index2";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<div id="list2">
<h2>分頁第二種實現(xiàn)方式——任務列表</h2>
<div class="table-responsive">
<table class="table table-striped">
<thead>
<tr>
<th>編號</th>
<th>名稱</th>
<th>描述</th>
<th>負責人</th>
<th>創(chuàng)建時間</th>
<th>完成時間</th>
<th>狀態(tài)</th>
</tr>
</thead>
<tbody data-bind="foreach:pagedList">
<tr>
<td data-bind="text: id"></td>
<td><a data-bind="text: name"></a></td>
<td data-bind="text: description"></td>
<td data-bind="text: owner"></td>
<td data-bind="text: creationTime"></td>
<td data-bind="text: finishTime"></td>
<td data-bind="text: state"></td>
</tr>
</tbody>
<tbody data-bind="if: loadingState">
<tr>
<td colspan="8" class="text-center">
<img width="60" src="/images/loading.gif" />
</td>
</tr>
</tbody>
<tfoot data-bind="ifnot:loadingState">
<tr>
<td colspan="8">
<div class="pull-right">
<div>總共有<span data-bind="text: totalCount"></span>條記錄, 每頁顯示:<span data-bind="text: pageSize"></span>條</div>
<div>
<ul class="pagination">
<li data-bind="css: { disabled: pageIndex() === 1 }"><a href="#" data-bind="click: previous">«</a></li>
</ul>
<ul data-bind="foreach: allPages" class="pagination">
<li data-bind="css: { active: $data.pageNumber === ($root.pageIndex()) }"><a href="#" data-bind="text: $data.pageNumber, click: function() { $root.gotoPage($data.pageNumber); }"></a></li>
</ul>
<ul class="pagination"><li data-bind="css: { disabled: pageIndex() === pageCount }"><a href="#" data-bind="click: next">»</a></li></ul>
</div>
</div>
</td>
</tr>
</tfoot>
</table>
</div>
</div>
對應的Js實現(xiàn)為:
// 實現(xiàn)分頁的第二種方式
var ListViewModel2 = function() {
//viewModel本身。用來防止直接使用this的時候作用域混亂
var self = this;
self.loadingState = ko.observable(true);
self.pageSize = ko.observable(3);
//數(shù)據(jù)
this.pagedList = ko.observableArray();
//要訪問的頁碼
this.pageIndex = ko.observable(1);
//總頁數(shù)
this.pageCount = ko.observable(1);
//頁碼數(shù)
this.allPages = ko.observableArray();
//當前頁
this.currengePage = ko.observable(1);
self.totalCount = ko.observable(1);
this.refresh = function() {
//限制請求頁碼在該數(shù)據(jù)頁碼范圍內(nèi)
if (self.pageIndex() < 1)
self.pageIndex(1);
if (self.pageIndex() > self.pageCount()) {
self.pageIndex(self.pageCount());
}
//post異步加載數(shù)據(jù)
sendAjaxRequest("GET", function (data) {
// 加載新的數(shù)據(jù)前,先移除原先的數(shù)據(jù)
self.pagedList.removeAll();
self.allPages.removeAll();
self.totalCount(data.totalCount);
self.pageCount(data.pageCount);
self.loadingState(false);
for (var i = 1; i <= data.pageCount; i++) {
//裝填頁碼
self.allPages.push({ pageNumber: i });
}
//for...in 語句用于對數(shù)組或者對象的屬性進行循環(huán)操作。
//for ... in 循環(huán)中的代碼每執(zhí)行一次,就會對數(shù)組的元素或者對象的屬性進行一次操作。
for (var i in data.pagedData) {
//裝填數(shù)據(jù)
self.pagedList.push(data.pagedData[i]);
}
}, 'GetByPaged', { 'pageIndex': self.pageIndex() });
};
//請求第一頁數(shù)據(jù)
this.first = function() {
self.pageIndex(1);
self.refresh();
};
//請求下一頁數(shù)據(jù)
this.next = function() {
self.pageIndex(this.pageIndex() + 1);
self.refresh();
};
//請求先前一頁數(shù)據(jù)
this.previous = function() {
self.pageIndex(this.pageIndex() - 1);
self.refresh();
};
//請求最后一頁數(shù)據(jù)
this.last = function() {
self.pageIndex(this.pageCount() - 1);
self.refresh();
};
//跳轉到某頁
this.gotoPage = function (data, event) {
self.pageIndex(data);
self.refresh();
};
};
function sendAjaxRequest(httpMethod, callback, url, reqData) {
$.ajax("/api/task" + (url ? "/" + url : ""), {
type: httpMethod,
success: callback,
data: reqData
});
}
$(document).ready(function () {
var viewModel = new ListViewModel2();
viewModel.refresh();
if ($('#list2').length)
ko.applyBindings(viewModel, $('#list2').get(0));
});
這里介紹了下使用KnockoutJs實現(xiàn)分頁功能的實現(xiàn)思路:
1.頁面加載完成之后,發(fā)起Ajax請求去異步調(diào)用REST 服務來請求部分數(shù)據(jù)。
2.然后將請求的數(shù)據(jù)通過KnockoutJs綁定顯示。
3.將對應的分頁信息綁定到Bootstrap分頁中
4.當用戶點擊翻頁時,再發(fā)起一個Ajax請求去異步調(diào)用Rest服務請求數(shù)據(jù),再將請求的數(shù)據(jù)顯示出來。
這上面是描述的代碼的調(diào)用邏輯關系,你可以參考對應的JS代碼來理解上面的描述。到此我們第二種實現(xiàn)方式就實現(xiàn)完成了。
2.2 第一次加載所有數(shù)據(jù),然后將所有數(shù)據(jù)分頁顯示
接下來就介紹了第一種實現(xiàn)方式,這樣的實現(xiàn)方式,用戶只會在第一次的時候才會感覺到數(shù)據(jù)加載中,翻頁過程中感覺不到頁面的加載,這樣對于一些本身數(shù)據(jù)了不是太多的情況下,對于用戶的感覺也是更加流暢的。
其具體的實現(xiàn)思路,也就是將請求的數(shù)據(jù)不要全部顯示在頁面上,因為數(shù)據(jù)太多,一下子顯示到頁面中,用戶可能會眼花繚亂。將數(shù)據(jù)分頁顯示將使得用戶查看更加清晰。
具體的Web前端Js的實現(xiàn)代碼為:
var ListViewModel = function () {
var self = this;
window.viewModel = self;
self.list = ko.observableArray();
self.pageSize = ko.observable(3);
self.pageIndex = ko.observable(0); //要訪問的頁碼
self.totalCount = ko.observable(1); //總記錄數(shù)
self.loadingState = ko.observable(true);
self.pagedList = ko.dependentObservable(function () {
var size = self.pageSize();
var start = self.pageIndex() * size;
return self.list.slice(start, start + size);
});
self.maxPageIndex = ko.dependentObservable(function () {
return Math.ceil(self.list().length / self.pageSize()) - 1;
});
self.previousPage = function () {
if (self.pageIndex() > 0) {
self.pageIndex(self.pageIndex() - 1);
}
};
self.nextPage = function () {
if (self.pageIndex() < self.maxPageIndex()) {
self.pageIndex(self.pageIndex() + 1);
}
};
self.allPages = ko.dependentObservable(function () {
var pages = [];
for (var i = 0; i <= self.maxPageIndex() ; i++) {
pages.push({ pageNumber: (i + 1) });
}
return pages;
});
self.moveToPage = function (index) {
self.pageIndex(index);
};
};
var listViewModel = new ListViewModel();
function bindViewModel() {
sendAjaxRequest("GET", function (data) {
listViewModel.loadingState(false);
listViewModel.list(data);
listViewModel.totalCount(data.length);
if ($('#list').length)
ko.applyBindings(listViewModel, $('#list').get(0));
}, null, null);
}
$(document).ready(function () {
bindViewModel();
});
其前端頁面的實現(xiàn)與前面的實現(xiàn)類似。具體頁面代碼如下:
@{
ViewBag.Title = "Index";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<div id="list">
<h2>任務列表</h2>
<div class="table-responsive">
<table class="table table-striped">
<thead>
<tr>
<th>編號</th>
<th>名稱</th>
<th>描述</th>
<th>負責人</th>
<th>創(chuàng)建時間</th>
<th>完成時間</th>
<th>狀態(tài)</th>
</tr>
</thead>
<tbody data-bind="foreach:pagedList">
<tr>
<td data-bind="text: id"></td>
<td><a data-bind="text: name"></a></td>
<td data-bind="text: description"></td>
<td data-bind="text: owner"></td>
<td data-bind="text: creationTime"></td>
<td data-bind="text: finishTime"></td>
<td data-bind="text: state"></td>
</tr>
</tbody>
<tbody data-bind="if:loadingState">
<tr>
<td colspan="8" class="text-center">
<img width="60" src="/images/loading.gif" />
</td>
</tr>
</tbody>
<tfoot data-bind="ifnot:loadingState">
<tr>
<td colspan="8">
<div class="pull-right">
<div>總共有<span data-bind="text: totalCount"></span>條記錄, 每頁顯示:<span data-bind="text: pageSize"></span>條</div>
<div>
<ul class="pagination">
<li data-bind="css: { disabled: pageIndex() === 0 }"><a href="#" data-bind="click: previousPage">«</a></li>
</ul>
<ul data-bind="foreach: allPages" class="pagination">
<li data-bind="css: { active: $data.pageNumber === ($root.pageIndex() + 1) }"><a href="#" data-bind="text: $data.pageNumber, click: function() { $root.moveToPage($data.pageNumber-1); }"></a></li>
</ul>
<ul class="pagination"><li data-bind="css: { disabled: pageIndex() === maxPageIndex() }"><a href="#" data-bind="click: nextPage">»</a></li></ul>
</div>
</div>
</td>
</tr>
</tfoot>
</table>
</div>
</div>
三、運行效果
接下來,讓我們看看,使用KnockoutJs實現(xiàn)的分頁效果:
四、總結
到這里,本文要介紹的內(nèi)容就結束,盡管本文實現(xiàn)的內(nèi)容相對比較簡單,但是對于一些剛接觸KnockoutJs的朋友來說,相信本文的實現(xiàn)會是一個很多的指導。接下來,我將會為大家分享下AngularJs的相關內(nèi)容。
以上所述是小編給大家介紹的Bootstrap與KnockoutJs相結合實現(xiàn)分頁效果實例詳解,希望對大家有所幫助!