2018年2月6日 星期二

[Vue] Infinite scrolling page

 Vue.js   Infinite scrolling   RxJS  



Introduction


We will implement an infinite scrolling page in vue.js and RxJS.


Environment

vue.js 2.5.13
RxJS 5.5.6



Implement


What we should do
1.  Backend data (Of course! But I will skip this part.)
2.  Observe the scroll down event and when the bottom is touched, get the new data to display.
3.  Current (or next) displayed data range, such as “first” and “last” flags, which are the parameters for getting new data in 2.


Observe the scroll event


let observable$ = Rx.Observable.fromEvent(window, 'scroll').map(x => x);

let subscription$ = observable$
                .map(x => Rx.Observable.of(x).delay(500))
                .switch()
                .subscribe(ev => {
if ((window.innerHeight + window.pageYOffset) >= document.body.offsetHeight) {
//TODO: When scroll to bottom, get the new data
}
                });
}

Use map, delay and switch to observe the window.onscroll and subscribe a receiving-new-data action if a user scrolls to bottom.

You can use the following codes to bind a callback on window.onscroll without RxJS.

window.onscroll = function (ev) {
     if ((window.innerHeight + window.pageYOffset) >= document.body.offsetHeight) {
           //console.log('Already scroll to bottom!');
     }
};


Current range of data

Let’s create
1.  A constant variable to store the default “first” and “last” and “max”.
Notice that “max” is the maximal number of data every time we get from backend.
2.  Current/Next “first” and “last”

For example, default (first, last)=(1,100), while max = 100, the next range will be (101,200).

const constRowLimit = { first: 1, last: 1000, max: 1000 };
rowLimit: { first: constRowLimit.first, last: constRowLimit.last, isLast: false }


Receiving new data

Last step, use the above variable rowLimit, to get the data from backend. In this example, I use OData to get them with Id located between rowLimit.first and rowLimit.last.

_renderData = function () {
           
getUri = serverUri.concat("?$filter=Id ge ", rowLimit.first, " and Id le ", rowLimit.last);

           //Set next range
           rowLimit.first += constRowLimit.max;
           rowLimit.last += constRowLimit.max;

axios.get(getUri)
                .then(function (response) {
                     //Append response.data to our model
                });
        },




Full sample codes

JS
const constRowLimit = { first: 1, last: 1000, max: 1000 };

var app = new Vue({
    el: '#app',
data: {
        books: [],
        rowLimit: { first: constRowLimit.first, last: constRowLimit.last, isLast: false }
    },
    methods: {
        _renderData: function () {
            var vm = this;

            let isFirstRender = true;
           
           if (vm.rowLimit.first !== constRowLimit.first)
                 isFirstRender = false;

           getUri = vm.$serverUri.concat("?$filter=Id ge ", vm.rowLimit.first, " and Id le ", vm.rowLimit.last);

           //Set next range
           vm.rowLimit.first += constRowLimit.max;
           vm.rowLimit.last += constRowLimit.max;

            //判斷是否已無資料
            if (vm.rowLimit.isLast === true)
                return;

            axios.get(getUri)
                .then(function (response) {
if (!response.data || response.data.length === 0) {
                        vm.rowLimit.isLast = true;
                    }
                    else {
                        response.data.forEach(x => {
                            vm.books.push(x);
                        });
}
                });
        },
        //When scroll to bottom, get the new data
        _observeScollToBottom: function () {
            var vm = this;
            let observable$ = Rx.Observable.fromEvent(window, 'scroll').map(x => x);

            let subscription$ = observable$
                .map(x => Rx.Observable.of(x).delay(500))
                .switch()
                .subscribe(ev => {

                    if ((window.innerHeight + window.pageYOffset) >= document.body.offsetHeight) {
                        vm._renderDictionaries(vm.keyword, vm.rowLimit.first, vm.rowLimit.last);
                    }
                });
    },
    mounted: function () {
        var vm = this;
        vm._observeScollToBottom();
        vm._renderDictionaries();
    }
})



HTML

<any v-for="bok in books">
</any>



Demo






Reference







沒有留言:

張貼留言