10

I have an array of articles in my Model and they are rendered nicely as HTML. What I want is to add some new articles when the user scrolls to the end of the page. I achieved this, but in my opinion with some really hacky behavior: all I have done is added jquery event handler $(window).scroll, like this:

function ArticlesViewModel() {
    var self                = this;
    this.listOfReports      = ko.observableArray([]);

    this.loadReports = function() {
        $.get('/router.php', {type: 'getReports'}, function(data){
            self.listOfReports(self.listOfReports().concat(data));
        }, 'json');
    };

    this.loadReports();

    $(window).scroll(function() {
        if($(window).scrollTop() == $(document).height() - $(window).height()) {
            self.loadReports();
        }
    })
};

In my simple prototype scenario it works nicely, but I think that this scroll will be called even if I will hide my model.

So is there a more appropriate way to do the same behavior?

Salvador Dali
  • 182,715
  • 129
  • 638
  • 708
  • Interesting question (though maybe not well suited for SO's Q&A format). I guess you're after an "infinite scrolling" solution, yeah? If you [search for those terms](https://www.google.nl/search?q=knockoutjs+infinite+scrolling) a few open source alternatives pop up as well. – Jeroen Feb 21 '14 at 13:13
  • @Jeroen I do not see why exactly it is not a good for SO, but yes I am after an "infinite scroll". Thank you for showing me what to look for. – Salvador Dali Feb 21 '14 at 19:46

2 Answers2

18

Because no one has answered my question, but Jeroen gave me a hint where to look at, I will attempt to answer my question with what I have found. So:

1) You have to use scroll event

View

<div id="main" data-bind="foreach: items, event: { scroll: scrolled }">
    <div data-bind="text: name"></div>
</div>

ViewModel

var viewModel = {
    items: ko.observableArray([]),
    scrolled: function(data, event) {
        var elem = event.target;
        if (elem.scrollTop > (elem.scrollHeight - elem.offsetHeight - 200)) {
            getItems(20);
        }
    },
    maxId: 0,
    pendingRequest: ko.observable(false)
};

function getItems(cnt) {
    if (!viewModel.pendingRequest()) {
        var entries = [];
        for (var i = 0; i < cnt; i++) {
            var id = viewModel.maxId++;
            entries.push({
                id: id,
                name: "Name" + id
            });
        }
        viewModel.pendingRequest(true);
        $.ajax({
            type: 'POST',
            url: '/echo/json/',
            data: {json: ko.toJSON(entries), delay: .1},
            success: function(entries) {
                ko.utils.arrayForEach(entries, function(entry) {
                    viewModel.items.push(entry);
                });
                viewModel.pendingRequest(false);
            },
            error: function() {
                viewModel.pendingRequest(false);
            },
            dataType: 'json'
        });
    }
}
ko.applyBindings(viewModel);
getItems(20);

Was taken from here and similar approaches with scrollOptions here.

There is also a nice MIT-license implementation here.

t1nr2y
  • 616
  • 8
  • 20
Salvador Dali
  • 182,715
  • 129
  • 638
  • 708
  • What does the `scrolled` function look like? – jjnguy Mar 30 '16 at 03:44
  • @jjnguy Late to the party but the scrolled function is right there in the code: `scrolled: function(data, event) { var elem = event.target; if (elem.scrollTop > (elem.scrollHeight - elem.offsetHeight - 200)) { getItems(20); } }` – Tobberoth Feb 03 '17 at 09:52
1

There is no "correct way", there are many different ways to implement infinite scroll in KnockoutJS, but I would suggest using the Knockout JS (KO) Infinite Scroll extender by thinkloop which you can find here: https://github.com/thinkloop/knockout-js-infinite-scroll

System24 Tech
  • 397
  • 4
  • 9