Bootstrap与KnockoutJs相结合实现分页效果实例详解
来源: 阅读:1046 次 日期:2016-07-06 10:48:02
温馨提示: 小编为您整理了“Bootstrap与KnockoutJs相结合实现分页效果实例详解”,方便广大网友查阅!

KnockoutJS是一个JavaScript实现的MVVM框架。接下来通过本文给大家介绍Bootstrap与KnockoutJs相结合实现分页效果,对bootstrap knockoutjs相关知识感兴趣的朋友一起学习吧

KnockoutJS是一个JavaScript实现的MVVM框架。非常棒。比如列表数据项增减后,不需要重新刷新整个控件片段或自己写JS增删节点,只要预先定义模板和符合其语法定义的属性即可。简单的说,我们只需要关注数据的存取。

一、引言

由于最近公司的系统需要改版,改版的新系统我打算使用KnockoutJs来制作Web前端。在做的过程中,遇到一个问题——如何使用KnockoutJs来完成分页的功能。在前一篇文章中并没有介绍使用KnockoutJs来实现分页,所以在这篇文章中,将补充用KnockoutJs+Bootstrap来实现数据的分页显示。

二、使用KnockoutJs实现分页

这里采用了两种方式来实现分页,第一种是将所有数据加载出来,然后再将所有数据分页显示;第二种是每次都只加载部分数据,每次请求都重新加载后面的数据。

对于这两种方式,使用Razor方式实现的分页一般都会采用第二种方式来实现分页,但是对于单页面程序来说,第一种实现方式也有其好处,对于不是非常大量的数据完全可以采用第一种实现方式,因为这样的话,后面的数据的加载,用户体验非常的流畅。所以这里将分别介绍这两种实现方式。

2.1 每次加载部分数据的实现

这里的后端代码采用的是前一篇文章的代码,只是多加了一些示例数据而已。具体的后端实现代码为:

/// <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

 }

Web前端的实现代码:

@{

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>

对应的Js实现为:

// 实现分页的第二种方式

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));

});

这里介绍了下使用KnockoutJs实现分页功能的实现思路:

1.页面加载完成之后,发起Ajax请求去异步调用REST 服务来请求部分数据。

2.然后将请求的数据通过KnockoutJs绑定显示。

3.将对应的分页信息绑定到Bootstrap分页中

4.当用户点击翻页时,再发起一个Ajax请求去异步调用Rest服务请求数据,再将请求的数据显示出来。

这上面是描述的代码的调用逻辑关系,你可以参考对应的JS代码来理解上面的描述。到此我们第二种实现方式就实现完成了。

2.2 第一次加载所有数据,然后将所有数据分页显示

接下来就介绍了第一种实现方式,这样的实现方式,用户只会在第一次的时候才会感觉到数据加载中,翻页过程中感觉不到页面的加载,这样对于一些本身数据了不是太多的情况下,对于用户的感觉也是更加流畅的。

其具体的实现思路,也就是将请求的数据不要全部显示在页面上,因为数据太多,一下子显示到页面中,用户可能会眼花缭乱。将数据分页显示将使得用户查看更加清晰。

具体的Web前端Js的实现代码为:

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>

三、运行效果

接下来,让我们看看,使用KnockoutJs实现的分页效果:

名单

四、总结

到这里,本文要介绍的内容就结束,尽管本文实现的内容相对比较简单,但是对于一些刚接触KnockoutJs的朋友来说,相信本文的实现会是一个很多的指导。接下来,我将会为大家分享下AngularJs的相关内容。

以上所述是小编给大家介绍的Bootstrap与KnockoutJs相结合实现分页效果实例详解,希望对大家有所帮助!

更多信息请查看网络编程
由于各方面情况的不断调整与变化, 提供的所有考试信息和咨询回复仅供参考,敬请考生以权威部门公布的正式信息和咨询为准!

2025国考·省考课程试听报名

  • 报班类型
  • 姓名
  • 手机号
  • 验证码
关于我们 | 联系我们 | 人才招聘 | 网站声明 | 网站帮助 | 非正式的简要咨询 | 简要咨询须知 | 加入群交流 | 手机站点 | 投诉建议
工业和信息化部备案号:滇ICP备2023014141号-1 云南省教育厅备案号:云教ICP备0901021 滇公网安备53010202001879号 人力资源服务许可证:(云)人服证字(2023)第0102001523号
云南网警备案专用图标
联系电话:0871-65317125(9:00—18:00) 获取招聘考试信息及咨询关注公众号:
咨询QQ:526150442(9:00—18:00)版权所有:
云南网警报警专用图标
Baidu
map