KnockoutJS ialah rangka kerja MVVM yang dilaksanakan dalam JavaScript. sangat sejuk. Sebagai contoh, selepas item data senarai ditambah atau dipadamkan, tidak perlu memuat semula keseluruhan serpihan kawalan atau menulis JS untuk menambah atau memadam nod sendiri Anda hanya perlu pra-takrif templat dan atribut yang mematuhi sintaksnya takrifan. Ringkasnya, kita hanya perlu fokus pada capaian data.
1
2. Gunakan KnockoutJs untuk melaksanakan paging
2.1 Pelaksanaan memuatkan data separa setiap kali
/// <summary> /// Web API 服务,为Web前端提供数据服务 /// </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 }; //返回数据 return pageData; } } /// <summary> /// 任务仓储,封装了所有关于数据库的操作 /// </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 = "创建一个SPA程序", Description = "SPA(single page web application),SPA的优势就是少量带宽,平滑体验", 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网站", Description = "Glimpse是一款.NET下的性能测试工具,支持asp.net 、asp.net mvc, EF等等,优势在于,不需要修改原项目任何代码,且能输出代码执行各个环节的执行时间", 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 }
@{ ViewBag.Title = "Index2"; Layout = "~/Views/Shared/_Layout.cshtml"; } <div id="list2"> <h2>分页第二种实现方式——任务列表</h2> <div class="table-responsive"> <table class="table table-striped"> <thead> <tr> <th>编号</th> <th>名称</th> <th>描述</th> <th>负责人</th> <th>创建时间</th> <th>完成时间</th> <th>状态</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>
// 实现分页的第二种方式 var ListViewModel2 = function() { //viewModel本身。用来防止直接使用this的时候作用域混乱 var self = this; self.loadingState = ko.observable(true); self.pageSize = ko.observable(3); //数据 this.pagedList = ko.observableArray(); //要访问的页码 this.pageIndex = ko.observable(1); //总页数 this.pageCount = ko.observable(1); //页码数 this.allPages = ko.observableArray(); //当前页 this.currengePage = ko.observable(1); self.totalCount = ko.observable(1); this.refresh = function() { //限制请求页码在该数据页码范围内 if (self.pageIndex() < 1) self.pageIndex(1); if (self.pageIndex() > self.pageCount()) { self.pageIndex(self.pageCount()); } //post异步加载数据 sendAjaxRequest("GET", function (data) { // 加载新的数据前,先移除原先的数据 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 语句用于对数组或者对象的属性进行循环操作。 //for ... in 循环中的代码每执行一次,就会对数组的元素或者对象的属性进行一次操作。 for (var i in data.pagedData) { //装填数据 self.pagedList.push(data.pagedData[i]); } }, 'GetByPaged', { 'pageIndex': self.pageIndex() }); }; //请求第一页数据 this.first = function() { self.pageIndex(1); self.refresh(); }; //请求下一页数据 this.next = function() { self.pageIndex(this.pageIndex() + 1); self.refresh(); }; //请求先前一页数据 this.previous = function() { self.pageIndex(this.pageIndex() - 1); self.refresh(); }; //请求最后一页数据 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)); });
1. Selepas halaman dimuatkan, mulakan permintaan Ajax untuk memanggil perkhidmatan REST secara tidak segerak untuk meminta beberapa data.
2.2 Muatkan semua data buat kali pertama, dan kemudian paparkan semua data dalam halaman
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); //总记录数 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(); });
@{ 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>创建时间</th> <th>完成时间</th> <th>状态</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>
3. Kesan operasi
4. Ringkasan