如何在 Knockout js 中提供分页功能

4

JSON数据(test.json)

[{
    "name": "A Goofy Movie (1995) 720p HDTVRip x264 Eng Subs [Dual Audio] [Hindi DD 2.0 - English DD 2.0] Exclusive By -=!Dr.STAR!=-",
    "progress": 0,
    "size": "1.06 GB",
    "downloaded": "87.98 KB",
    "hash": "8fe65e43464debe1bc0961e4656ea780368d4fba",
    "peer": 0,
    "delete": "delete.php?del=8fe65e43464debe1bc0961e4656ea780368d4fba",
    "speed": "0 Byte",
    "eta": "23:59:59",
    "path": "C:\\xampp\\htdocs\\final\\download\/8fe65e43464debe1bc0961e4656ea780368d4fba"
}, {
    "name": "Logan+2017+HDCAM+XviD+UnKnOwN",
    "progress": 0,
    "size": "0 Byte",
    "downloaded": "0 Byte",
    "hash": "624911f8e4fc172e5ed7970d3bc097198bfd4e76",
    "peer": 0,
    "delete": "delete.php?del=624911f8e4fc172e5ed7970d3bc097198bfd4e76",
    "speed": "0 Byte",
    "eta": "23:59:59",
    "path": "C:\\xampp\\htdocs\\final\\download\/624911f8e4fc172e5ed7970d3bc097198bfd4e76"
}, {
    "name": "Internet Download Manager 6.27 Build 5 Multilingual + Patch",
    "progress": 100,
    "size": "6.97 MB",
    "downloaded": "7.49 MB",
    "hash": "bffe600ae08ba8e55db30dae6acd86979e30ce15",
    "peer": 0,
    "delete": "delete.php?del=bffe600ae08ba8e55db30dae6acd86979e30ce15",
    "speed": "0 Byte",
    "eta": "23:59:59",
    "path": "C:\\xampp\\htdocs\\final\\download\/bffe600ae08ba8e55db30dae6acd86979e30ce15"
}]

KnockoutJS 数据

function ExampleViewModel() {
    var self = this;
    self.ExampleData = ko.observableArray([]);
    self.update = function() {
        $.ajax("test.json", {
            success: function(allData) {
                var mappeddata = $.map(allData, function(item) {
                    return new DataItem(item)
                });
                self.ExampleData(mappeddata);
            }
        });
    }
}

function DataItem(data) {
    ko.observable(data.name);
    ko.observable(data.progress);
}
var exampleViewModel = new ExampleViewModel();
ko.applyBindings(exampleViewModel);

如何提供分页并仅显示前两个值?

工作示例


可能是如何使用Knockout处理分页的重复问题。 - Hakan Fıstık
2个回答

1
你可以通过下面的self.Data循环遍历。
function ExampleViewModel() {
  var self = this;
  self.ExampleData = ko.observableArray([]);
  self.CurrentPage = ko.observable(1);
  self.DataPerPage = ko.observable(2); // You can change from here if you want to show the data other than 2 per page
  self.Data = ko.pureComputed(function(){
   var startIndex = self.CurrentPage() === 1? 0 : (self.CurrentPage() - 1) * self.DataPerPage();
   return self.ExampleData().slice(startIndex, startIndex + self.DataPerPage())
  });
  self.update = function() {
    $.ajax("test.json", {
      success: function(allData) {
        var mappeddata = $.map(allData, function(item) {
          return new DataItem(item)
        });
        self.ExampleData(mappeddata);
      }
    });
  }
}

简单示例:

function ViewModel() {
  var self = this;
  self.ExampleData = ko.observableArray([1, 2, 3, 4, 5, 6, 7, 8, 9]);
  self.CurrentPage = ko.observable(1); // Store the current page of the user
  self.DataPerPage = ko.observable(2); // To identify how many data we want to see per page

  self.Data = ko.pureComputed(function(){
   var startIndex = self.CurrentPage() === 1? 0 : (self.CurrentPage() - 1) * self.DataPerPage();
   return self.ExampleData().slice(startIndex, startIndex + self.DataPerPage ())
  });

  self.Next = function() {
    var totalData = self.ExampleData().length;
    var currentPage = self.CurrentPage();

    // if the length is 0, don't allow next
    // if we're on the last page, don't allow next
    if(totalData >= 1 && currentPage < (Math.ceil(totalData/2))) self.CurrentPage(currentPage + 1);
  };

  self.Prev = function() {
    var currentPage = self.CurrentPage();
    
    // if we're on the first page, don't allow prev
    if(currentPage > 1) self.CurrentPage(currentPage - 1);
  };
}   

$(document).ready(function () {
  var myViewModel = new ViewModel();
  ko.applyBindings(myViewModel);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>

<div>
  <ul data-bind="foreach: Data">
    <li data-bind="text: $data"></li>
  </ul>
  <button data-bind="click: Next">Next</button>
  <button data-bind="click: Prev ">Prev</button>
</div>


未捕获的 TypeError: ko.pureComputed 不是一个函数。 - Amitya
@Amitya 尝试使用 ko.computed。 - muhihsan
很抱歉,先生,它显示了所有的数据而不是前两个数据,我也从您的示例中开始学习,但它并没有帮助我。 - Amitya
你是从我创建的代码中复制粘贴的吗?你能把它放到 jsfiddle 上并分享吗? - muhihsan
你应该在“DataPerPage”上使用foreach,而不是“ExampleData”。 - muhihsan
显示剩余3条评论

0

实际上我正在开发一个网站,其中有很多表格(大部分需要分页),所以我需要一些可重用的分页组件,以便在所有需要分页的情况下使用。

此外,我需要比这个问题的接受答案提供的更高级的功能。

因此,我开发了自己的组件来解决这个问题,这就是它。

现在在Github上

JsFiddle

更多详情请继续阅读

JavaScript

function PagingVM(options) {
    var self = this;

    self.PageSize = ko.observable(options.pageSize);
    self.CurrentPage = ko.observable(1);
    self.TotalCount = ko.observable(options.totalCount);

    self.PageCount = ko.pureComputed(function () {
        return Math.ceil(self.TotalCount() / self.PageSize());
    });

    self.SetCurrentPage = function (page) {
        if (page < self.FirstPage)
            page = self.FirstPage;

        if (page > self.LastPage())
            page = self.LastPage();

        self.CurrentPage(page);
    };

    self.FirstPage = 1;
    self.LastPage = ko.pureComputed(function () {
        return self.PageCount();
    });

    self.NextPage = ko.pureComputed(function () {
        var next = self.CurrentPage() + 1;
        if (next > self.LastPage())
            return null;
        return next;
    });

    self.PreviousPage = ko.pureComputed(function () {
        var previous = self.CurrentPage() - 1;
        if (previous < self.FirstPage)
            return null;
        return previous;
    });

    self.NeedPaging = ko.pureComputed(function () {
        return self.PageCount() > 1;
    });

    self.NextPageActive = ko.pureComputed(function () {
        return self.NextPage() != null;
    });

    self.PreviousPageActive = ko.pureComputed(function () {
        return self.PreviousPage() != null;
    });

    self.LastPageActive = ko.pureComputed(function () {
        return (self.LastPage() != self.CurrentPage());
    });

    self.FirstPageActive = ko.pureComputed(function () {
        return (self.FirstPage != self.CurrentPage());
    });

    // this should be odd number always
    var maxPageCount = 7;

    self.generateAllPages = function () {
        var pages = [];
        for (var i = self.FirstPage; i <= self.LastPage() ; i++)
            pages.push(i);

        return pages;
    };

    self.generateMaxPage = function () {
        var current = self.CurrentPage();
        var pageCount = self.PageCount();
        var first = self.FirstPage;

        var upperLimit = current + parseInt((maxPageCount - 1) / 2);
        var downLimit = current - parseInt((maxPageCount - 1) / 2);

        while (upperLimit > pageCount) {
            upperLimit--;
            if (downLimit > first)
                downLimit--;
        }

        while (downLimit < first) {
            downLimit++;
            if (upperLimit < pageCount)
                upperLimit++;
        }

        var pages = [];
        for (var i = downLimit; i <= upperLimit; i++) {
            pages.push(i);
        }
        return pages;
    };

    self.GetPages = ko.pureComputed(function () {
        self.CurrentPage();
        self.TotalCount();

        if (self.PageCount() <= maxPageCount) {
            return ko.observableArray(self.generateAllPages());
        } else {
            return ko.observableArray(self.generateMaxPage());
        }
    });

    self.Update = function (e) {
        self.TotalCount(e.TotalCount);
        self.PageSize(e.PageSize);
        self.SetCurrentPage(e.CurrentPage);
    };

    self.GoToPage = function (page) {
        if (page >= self.FirstPage && page <= self.LastPage())
            self.SetCurrentPage(page);
    }

    self.GoToFirst = function () {
        self.SetCurrentPage(self.FirstPage);
    };

    self.GoToPrevious = function () {
        var previous = self.PreviousPage();
        if (previous != null)
            self.SetCurrentPage(previous);
    };

    self.GoToNext = function () {
        var next = self.NextPage();
        if (next != null)
            self.SetCurrentPage(next);
    };

    self.GoToLast = function () {
        self.SetCurrentPage(self.LastPage());
    };
}

HTML

<ul data-bind="visible: NeedPaging" class="pagination pagination-sm">
    <li data-bind="css: { disabled: !FirstPageActive() }">
        <a data-bind="click: GoToFirst">First</a>
    </li>
    <li data-bind="css: { disabled: !PreviousPageActive() }">
        <a data-bind="click: GoToPrevious">Previous</a>
    </li>

    <!-- ko foreach: GetPages() -->
    <li data-bind="css: { active: $parent.CurrentPage() === $data }">
        <a data-bind="click: $parent.GoToPage, text: $data"></a>
    </li>
    <!-- /ko -->

    <li data-bind="css: { disabled: !NextPageActive() }">
        <a data-bind="click: GoToNext">Next</a>
    </li>
    <li data-bind="css: { disabled: !LastPageActive() }">
        <a data-bind="click: GoToLast">Last</a>
    </li>
</ul>

特点

  1. 按需显示
    当根本不需要分页时(例如需要显示的项目少于页面大小),则HTML组件将消失。
    这将通过语句data-bind="visible: NeedPaging"来实现。

  2. 按需禁用
    例如,如果您已经选择了最后一页,为什么还应该可以按下last pageNext按钮?
    我正在处理这个问题,在这种情况下,我通过应用以下绑定来禁用这些按钮 data-bind="css: { disabled: !PreviousPageActive() }"

  3. 区分所选页面
    在所选页面上应用特殊类(在本例中称为active类),以使用户知道他/她现在在哪个页面。
    这是通过绑定data-bind="css: { active: $parent.CurrentPage() === $data }"来实现的。

  4. 第一个和最后一个
    通过专门的按钮可以轻松地转到第一个和最后一个页面。

  5. 显示按钮的限制
    假设您有很多页面,例如1000个页面,那么会发生什么?您会全部显示给用户吗?当然不会,您只需根据当前页面显示其中一些。例如,在所选页面之前显示3页,其他3页在所选页面之后。
    这种情况已在此处处理<!-- ko foreach: GetPages() -->
    GetPages函数应用简单的算法来确定是否需要显示所有页面(页面计数低于阈值,可以轻松确定),或者仅显示某些按钮。
    您可以通过更改maxPageCount变量的值来确定阈值
    现在我将其分配为以下内容var maxPageCount = 7;,这意味着最多只能为用户显示7个按钮(所选页面之前的3个和所选页面之后的3个以及所选页面本身)。

    您可能会想知道,如果在当前页面之后之前没有足够的页面来显示会怎样?不要担心,我正在算法中处理这个问题
    例如,如果您有11个页面,并且您有maxPageCount = 7,当前selected page是10,则将显示以下页面

    5,6,7,8,9,10(selected page),11

    因此,我们始终满足maxPageCount,在上一个示例中显示所选页面之前的5页和所选页面之后的1页。

  6. 所选页面验证
    所有设置CurrentPage可观察对象(由用户确定所选页面)的操作都通过函数SetCurrentPage进行。仅在此函数中设置此可观察对象,并且从代码中可以看出,在设置值之前,我们执行验证操作以确保不会超出页面的可用页面。

  7. 已清除
    我只使用pureComputed而不是computed属性,这意味着您无需担心清理和处理这些属性

    注意1
    您可能已经注意到我在此组件中使用了一些bootstrap类,这对我来说很合适,但是当然您也可以使用自己的类代替 bootstrap 类。
    我在这里使用的 bootstrap 类包括paginationpagination-smactivedisabled
    随意根据需要更改它们。

    注意2
    现在我为您介绍了这个组件,是时候看看它如何工作了。
    您可以像这样将此组件集成到您的主 ViewModel 中。

    function MainVM() {
        var self = this;
    
        self.PagingComponent = ko.observable(new Paging({
            pageSize: 10,      // how many items you would show in one page
            totalCount: 100,   // how many ALL the items do you have.
        }));
    
        self.currentPageSubscription = self.PagingComponent().CurrentPage.subscribe(function (newPage) {
            // here is the code which will be executed when the user change the page.
            // you can handle this in the way you need.
            // for example, in my case, I am requesting the data from the server again by making an ajax request
            // and then updating the component
    
            var data = /*bring data from server , for example*/
            self.PagingComponent().Update({
    
                // we need to set this again, why? because we could apply some other search criteria in the bringing data from the server, 
                // so the total count of all the items could change, and this will affect the paging
                TotalCount: data.TotalCount,
    
                // in most cases we will not change the PageSize after we bring data from the server
                // but the component allow us to do that.
                PageSize: self.PagingComponent().PageSize(),
    
                // use this statement for now as it is, or you have to made some modifications on the 'Update' function.
                CurrentPage: self.PagingComponent().CurrentPage(),
            });
        });
    
        self.dispose = function () {
            // you need to dispose the manual created subscription, you have created before.
            self.currentPageSubscription.dispose();
        }
    }
    

    最后但并非最不重要的是,请务必根据您特定的viewModel更改html组件中的绑定,或者像这样用with binding包装所有组件。
    <div data-bind="with: PagingComponent()">
        <!-- put the component here -->
    </div>
    

    干杯


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接