Infinite scrolling load with Angular and RxJS

Source: Internet
Author: User

What should an infinite scrolling load be?

The Infinite scrolling load list loads the data asynchronously when the user scrolls the page to the specified location. This is a good way to avoid the need for active loading (which requires the user to click each time), and it can really keep the performance of the app. It is also an effective way to reduce bandwidth and enhance the user experience.

For this scenario, it is assumed that each page contains 10 data, and that all data is displayed in a long, scrollable list, which is the infinite scrolling load list.

Let's make a list of the features that the infinite scroll load lists must satisfy:

    • The first page of data should be loaded by default
    • When the first page of the data can not be fully populated with the initial screen, you should load the second pages of data, and so on, until the first screen filled
    • When the user scrolls down, the third page of data should be loaded, and so on
    • When the user resizes the window, there is more space to show the results, and the next page of data should be loaded
    • You should ensure that the same page data is not loaded two times (cached)
First Draw

Just like most coding decisions, it's a good idea to draw on the whiteboard first. This may be a personal way, but it helps me write out the code without being deleted or refactored at a later stage.

According to the list of features listed above, there are three actions that enable the app to trigger loading data: Scrolling, resizing windows, and manually triggering data loading. When we think with responsive thinking, we can see that there are 3 of events in the source, which we call a flow:

    • Stream of the Scroll event: scroll$
    • Stream of the Resize event: resize$
    • Manually determine the flow of page data: pagebymanual$

Note: We will add a suffix $ to the rheological amount to indicate that this is a flow, which is a convention (which the individual also prefers this way)

We draw these streams on the whiteboard:

Over time, these flows will contain specific values:

scroll$The stream contains the Y value, which is used to calculate the page number.

resize$The stream contains an event value. We don't need the value itself, but we need to know that the user is resizing the window.

pageByManual$Contains the page number because it is a Subject, so we can set it directly. (We'll talk later)

What if we could map all these streams to a stream of page numbers? That would be great, because page numbering is the only way to load data for a specified page. So how do you map the current stream into a stream of page numbers? This is not something we need to consider now (we are just drawing, remember?). The next diagram looks like this:

As you can see, we created the following flow based on the initial flow:

    • pagebyscroll$: Contains page numbers based on scroll events
    • pagebyresize$: Contains page numbers based on resize events
    • pagebymanual$: Contains page numbers based on manual events (for example, if there is still a blank area on the page, we need to load the next page of data)

If we were able to merge the 3 page flows in a valid way, we would get a new stream pageToLoad$ called, which contains the page numbers created by the scroll event, the Resize event, and the manual event.

If we subscribe to the pageToLoad$ stream and not get the data from the service, then our infinite scrolling load is already partially working. But aren't we thinking in a responsive way? This means that you want to avoid subscriptions as much as possible ... In fact, we need to pageToLoad$ create a new stream based on the stream, which will contain the data from the Infinite scrolling list ...

Now combine these graphs into a comprehensive design.

As shown, we have 3 input streams: they are responsible for handling scrolling, resizing windows, and triggering manually. We then have 3 page flows based on the input stream and merge them into one stream, the pageToLoad$ stream. Based on pageToLoad$ the flow, we can get the data.

Start coding

The diagram has been drawn very well, and we have a clear understanding of what to do with the infinite scrolling list, so let's start coding.

To calculate the number of pages that need to be loaded, we need 2 properties:

private itemHeight = 40;private numberOfItems = 10; // 页面中的项数
pagebyscroll$

pageByScroll$The flow is as follows:

Private pagebyscroll$ =First, we're going to create a stream that contains all the scrolling events that occur on the Window object Observable.fromevent (Window"Scroll")We are only interested in the scrolly values of these events.So create a stream that contains only these values. Map (() =window.scrolly)//create a stream that contains only filtered values //we only need the value when we scroll in the viewport. Filter (current = current >=  Document.body.clientHeight-window.innerheight) // It is only after the user stops scrolling 200ms that we continue to execute //so add 200ms debounce time for this stream. Debouncetime ( 200) //filter out duplicate values. DISTINCT () // Calculates the page number. Map (y = math.ceil ((y + window.innerheight)/(this.itemheight * this.numberofitems))); //--------1---2----3------2 ...          

Note: In real-world applications, you may want to use window and document injection services

pagebyresize$

pageByResize$The flow is as follows:

  private pageByResize$ =   // 现在,我们要创建一个流,它包含发生在 window 对象上的所有 resize 事件Observable.fromEvent(window, "resize")  // 当用户停止操作200ms后,我们才继续执行.debounceTime(200) // 基于 window 计算页码 .map(_ => Math.ceil( (window.innerHeight + document.body.scrollTop) / (this.itemHeight * this.numberOfItems) )); // --------1---2----3------2...
pagebymanual$

pageByManual$The stream is used to get the initial value (the first screen data), but it also needs our manual control. BehaviorSubjectvery well, because we need a stream with an initial value, and we can add the value manually.

private pageByManual$ = new BehaviorSubject(1);// 1---2----3------...
pagetoload$

Cool, already has the input stream of 3 page numbers, now let's create a pageToLoad$ stream.

private pageToLoad$ =   // 将所有页码流合并成一个新的流Observable.merge(this.pageByManual$, this.pageByScroll$, this.pageByResize$) // 过滤掉重复的值.distinct() // 检查当前页码是否存在于缓存(就是组件里的一个数组属性)之中.filter(page => this.cache[page-1] === undefined); 
itemresults$

The hardest part has been done. Now we have a stream with a page number, which is very useful. We no longer need to be concerned with individual scenarios or other complex logic. Each time pageToLoad$ a stream has a new value, we just load the data. it's so simple!!

We will use the flatmap operator to do this, because the call data itself returns the stream as well. FlatMap (or Mergemap) will flatten the high-order Observable.

itemresults$ =this.pagetoload$Asynchronously loading data based on a page number streamFlatMap is the alias of Meregmap. FlatMap ((PageNumber) = {Load some of the characters in Star WarsReturnThis.http.get ( ' Https://swapi.co/api/people?page=${page} ') //Create a stream that contains this data. Map (RESP = Resp.json (). Results). Do (resp = {// Add page numbers to the cache this.cache[page -1] = resp; //if the page still has enough white space, continue loading the data:) if (( this.itemheight * this.numberofitems * page) < window.innerheight) {this.pagebymanual$.next (page + 1);})}) //Finally, only the stream containing the data cache is returned. Map (_ = = FlatMap (this.cache));    
Results

The complete code is as follows:

Note that async pipe is responsible for the entire subscription process .

@Component ({selector:' Infinite-scroll-list ', Template:' <table> <tbody> <tr *ngfor= ' let item of itemresults$ |  Async "[style.height]=" ItemHeight + ' px ' "> <td></td> </tr> </tbody> </table> `})ExportClass Infinitescrolllistcomponent {Private cache = [];Private pagebymanual$ =New Behaviorsubject (1);Private ItemHeight =40;Private Numberofitems =10;Private pagebyscroll$ = Observable.fromevent (Window"Scroll"). Map (() =window.scrolly). Filter (Current = Current >=Document.body.clientHeight-window.innerheight). Debouncetime (). Distinct (). Map (y =Math.ceil ((y +Window.innerheight)/(This.itemheight *(This.numberofitems)));Private pagebyresize$ = Observable.fromevent (Window"Resize"). Debouncetime (). Map (_ =Math.ceil ((Window.innerheight +DOCUMENT.BODY.SCROLLTOP)/(This.itemheight *(This.numberofitems)));Private pagetoload$ = Observable. Merge (this.pagebymanual$,this.pagebyscroll$,this.pagebyresize$). Distinct (). Filter (page =This.cache[page-1] = = =undefined); itemresults$ =this.pagetoload$. Do (_ =This.loading =true). FlatMap ((PageNumber) = {Returnthis.http.get (' https://swapi.co/api/people?page=${page} '). Map (resp = Resp.json (). Results). Do (resp = {this.cache[page -1] = resp;  if ((this.itemheight * this.numberofitems * page) < window.innerheight) {this.pagebymanual$. Next (page + 1);}) }). map (_ = = FlatMap (this.cache)); Constructor (private Http:http) {}}             

This is the address of the online example


Sangka
Links: https://juejin.im/post/5aaf8c3651882555627d17b9
Source: Denver Nuggets
Copyright belongs to the author. Commercial reprint please contact the author for authorization, non-commercial reprint please specify the source.

Infinite scrolling load with Angular and RxJS

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.