{"id":13480868,"url":"https://github.com/dinony/od-virtualscroll","last_synced_at":"2025-06-14T01:03:30.434Z","repository":{"id":57313424,"uuid":"89012658","full_name":"dinony/od-virtualscroll","owner":"dinony","description":"🚀 Observable-based virtual scroll implementation in Angular","archived":false,"fork":false,"pushed_at":"2018-11-29T22:04:46.000Z","size":126,"stargazers_count":134,"open_issues_count":11,"forks_count":12,"subscribers_count":13,"default_branch":"master","last_synced_at":"2025-05-13T04:07:38.201Z","etag":null,"topics":["angular","angular-component","aot-compilation","infinite-scroll","observable","od-virtualscroll","reactive-programming","rxjs","tileview","tiling","virtual-scroll","virtual-scroller"],"latest_commit_sha":null,"homepage":"https://dinony.github.io/od-vsstatic/","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dinony.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-04-21T18:30:52.000Z","updated_at":"2023-03-24T01:25:21.000Z","dependencies_parsed_at":"2022-09-20T23:11:39.923Z","dependency_job_id":null,"html_url":"https://github.com/dinony/od-virtualscroll","commit_stats":null,"previous_names":[],"tags_count":8,"template":false,"template_full_name":null,"purl":"pkg:github/dinony/od-virtualscroll","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dinony%2Fod-virtualscroll","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dinony%2Fod-virtualscroll/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dinony%2Fod-virtualscroll/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dinony%2Fod-virtualscroll/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dinony","download_url":"https://codeload.github.com/dinony/od-virtualscroll/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dinony%2Fod-virtualscroll/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":259693777,"owners_count":22897467,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["angular","angular-component","aot-compilation","infinite-scroll","observable","od-virtualscroll","reactive-programming","rxjs","tileview","tiling","virtual-scroll","virtual-scroller"],"created_at":"2024-07-31T17:00:46.033Z","updated_at":"2025-06-14T01:03:30.379Z","avatar_url":"https://github.com/dinony.png","language":"TypeScript","readme":"# od-virtualscroll\n\n[![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/dinony/od-virtualscroll/master/LICENSE) [![Module format](https://img.shields.io/badge/module%20formats-umd%2Fes2015%2Ffesm5%2Ffesm15-blue.svg)](https://github.com/dinony/od-virtualscroll#module-format) [![Module format](https://img.shields.io/badge/supports-AoT-red.svg)](https://github.com/dinony/od-virtualscroll#module-format)\n![gzip size](http://img.badgesize.io/https://unpkg.com/od-virtualscroll/dist/bundle/od-virtualscroll.min.umd.js?label=gzip%20size\u0026compression=gzip)\n\n\u003e Observable-based virtual scroll implementation in Angular.\n\n## Installation\n\n```\nnpm i -S od-virtualscroll\n```\n\n## Features\n\nLet's you scroll efficiently through a humongous list/grid of items (with single predefined height) by recycling components and minimizing component updates.\n\n- Handles resizing\n- Efficient\n  - Displays necessary amount of rows\n  - Optimal updates on data change or resize\n- Supports tiling\n- Supports fixed number of columns\n- Reactive component\n  - Observable interface for most parts\n- Supports AoT\n- API\n  - Subscribe to key component observables\n- Plus\n  - Debounce scrolling / resizing\n  - Set scroll position, focus row or item via index\n  - Customizable equality checking\n  - A lot of code samples\n- Module formats\n  - Ships FESM5 and FESM15\n  - Ships ES5/UMD, ES5/ES2015 and ES2015/ES2015 exports (`{{target}}/{{module}}`)\n\n## Demo\n\nAll examples are written in Angular 4 and provided in separate repositories to keep this repository simple.\n\n| Name           | Description\n|----------------|-------------------------------------\n| [od-vsstatic](https://github.com/dinony/od-vsstatic) / [Demo](https://dinony.github.io/od-vsstatic/)     | Static example with 100k cells. Ideal for performance analysis and GC testing\n| [od-vsdynamic](https://github.com/dinony/od-vsdynamic) / [Demo](https://dinony.github.io/od-vsdynamic/)  | Scroll through GIFs, without the risk of a CPU meltdown ([GIPHY API](https://api.giphy.com/))\n| [od-vslist](https://github.com/dinony/od-vslist) / [Demo](https://dinony.github.io/od-vslist/)     | Render only 1 cell per row with dynamic width ([randomuser API](https://randomuser.me/documentation))\n| [od-vsadvanced](https://github.com/dinony/od-vsadvanced) / [Demo](https://dinony.github.io/od-vsadvanced/) | Shows more advanced API features and utilizes the auxiliary debug module\n| [od-vscolors](https://github.com/dinony/od-vscolors) / [Demo](https://dinony.github.io/od-vscolors/)   | Just for fun\n\nHowever, this repository also holds a minimalistic demo, to allow local development and AoT compilation.\n\n## Usage\n\nImport the module and specify the cell and container styling (traditional layout or flexbox/... your choice).\n\n```typescript\n// app.module.ts\nimport {NgModule} from '@angular/core';\nimport {BrowserModule} from '@angular/platform-browser';\nimport {VirtualScrollModule} from 'od-virtualscroll';\nimport {AppComponent} from './app.component';\n\n@NgModule({\n  bootstrap: [AppComponent],\n  declarations: [AppComponent],\n  imports: [\n    BrowserModule,\n    VirtualScrollModule\n  ]\n})\nexport class AppModule {}\n\n// app.component.ts\nimport {Component} from '@angular/core';\nimport {Observable} from 'rxjs';\nimport {IVirtualScrollOptions} from 'od-virtualscroll';\n\n@Component({\n  selector: 'app-shell',\n  styles: [`...`],                      // \u003c-- Style your cell and container\n  template: `\n    \u003cod-virtualscroll [vsData]=\"data$\" [vsOptions]=\"options$\"\u003e\n      \u003cng-template let-item let-row=\"row\" let-column=\"column\"\u003e\n        \u003cspan\u003eRow: {{row}}\u003c/span\u003e\u003cbr\u003e\n        \u003cspan\u003eColumn: {{column}}\u003c/span\u003e\n        {{item}}\n      \u003c/ng-template\u003e\n    \u003c/od-virtualscroll\u003e`\n})\nexport class AppComponent {\n  data$: Observable\u003cany[]\u003e = ... ;                      // \u003c-- Define data\n  options$: Observable\u003cIVirtualScrollOptions\u003e = ... ;   // \u003c-- Define options\n}\n```\n\nIf you want to apply a traditional layout and wonder about the space between inline block elements - read [this](https://css-tricks.com/fighting-the-space-between-inline-block-elements/).\n\n## Inputs\n\n| Name             | Type                                              | Description\n|------------------|---------------------------------------------------|-------------------------------------------------------------------------------------------------------\n| vsData           | `Observable\u003cany[]\u003e`                               | Stream of data\n| vsOptions        | `Observable\u003cIVirtualScrollOptions\u003e`               | Stream of options\n| vsResize         | `Observable\u003cany\u003e`                                 | Stream of resize commands (optional, default: `-\\-\u003e`)\n| vsUserCmd        | `Observable\u003cIUserCmd\u003e`                            | Stream of user specific commands (optional, default: `-\\-\u003e`)\n| vsDebounceTime   | `number`                                          | Debounce scroll and resize events [ms] (optional, default: 0)\n| vsEqualsFunc     | `(prevIndex: number, curIndex:number) =\u003e boolean` | Function to determine equality, given two indicies in the array (optional, default: `(p,c) =\u003e p === c)`)\n\n### IVirtualScrollOptions\n\n```typescript\nexport interface IVirtualScrollOptions {\n  itemWidth?: number;\n  itemHeight: number;\n  numAdditionalRows?: number;\n  numLimitColumns?: number;\n}\n```\n\nThe component requires either fixed-size cells (itemWidth, itemHeight) or a fixed number of cells per row (itemHeight, numLimitColumns).\n\nFurther, to improve scrolling, additional rows may be requested.\n\n### IUserCmd\n\nCurrently, the supported user specific commands are:\n\n* `SetScrollTopCmd`: Set scroll top to specific value\n* `FocusRowCmd`: Focus specific row index\n* `FocusItemCmd`: Focus specific item index\n\nE.g. Focus row index 42.\n\n```typescript\ndata$ = // Data...;\nuserCmd$ = of(new FocusRowCmd(42)).pipe(delay(2000));\n```\n\n```html\n\u003cod-virtualscroll [vsData]=\"data$\" [vsUserCmd]=\"userCmd$\"\u003e\n  \u003c!-- Your template --\u003e\n\u003c/od-virtualscroll\u003e\n```\n\n## API\n\n### ScrollObservableService\nInject the *ScrollObservableService* to subscribe to key component observables.\n\n| Name             | Type                                                       | Description\n|------------------|------------------------------------------------------------|-------------------------------------------------------\n| scrollWin$       | `[IVirtualScrollWindow]`                                   | Stream of the most important inner data structure\n| createRow$       | `[CreateRowCmd,  ComponentRef\u003cVirtualRowComponent\u003e]`       | Create row command and ComponentRef\n| removeRow$       | `[RemoveRowCmd, ComponentRef\u003cVirtualRowComponent\u003e]`        | Remove row command and ComponentRef\n| shiftRow$        | `[ShiftRowCmd, ComponentRef\u003cVirtualRowComponent\u003e]`         | Shift row command and ComponentRef\n| createItem$      | `[CreateItemCmd, ScrollItem, EmbeddedViewRef\u003cScrollItem\u003e]` | Create item command, scroll item and EmbeddedViewRef\n| updateItem$      | `[UpdateItemCmd, ScrollItem, EmbeddedViewRef\u003cScrollItem\u003e]` | Update item command, scroll item and EmbeddedViewRef\n| removeItem$      | `[RemoveItemCmd]`                                          | Remove item command\n\nThe [od-vsdynamic](https://github.com/dinony/od-vsdynamic) and [od-vsadvanced](https://github.com/dinony/od-vsadvanced) examples show how the API may be used.\n\n### IVirtualScrollWindow\n\nThis interface provides pretty much all needed information.\n\n```typescript\nexport interface IVirtualScrollWindow {\n  dataTimestamp: number;\n  containerWidth: number;\n  containerHeight: number;\n  itemWidth?: number;\n  itemHeight: number;\n  numVirtualItems: number;\n  numVirtualRows: number;\n  virtualHeight: number;\n  numAdditionalRows: number;\n  scrollTop: number;\n  scrollPercentage: number;\n  numActualRows: number;\n  numActualColumns: number;\n  actualHeight: number;\n  numActualItems: number;\n  visibleStartRow: number;\n  visibleEndRow: number;\n}\n```\nIt is used internally and may also be useful in consuming application components.\n\nE.g.: The [od-vsdynamic](https://github.com/dinony/od-vsdynamic) example.\n\n### Multiple Instances\n\nThe `ScrollObservableService` is registered on the VirtualScrollModule by default, so it is available on the root injector.\nHowever, if you have multiple instances of the scroll component, a singleton instance of the `ScrollObservableService` is not enough.\nRegister the service on the wrapping component, via the providers property in the `@Component` decorator, so that the injector bubbling will stop on the Component level and will serve the right instance of the ScrollObservableService.\n\nCheck the [feature/testMultiInstances](https://github.com/dinony/od-virtualscroll/tree/feature/testMultiInstances) branch for a simple example.\n\n### Further information\n\n[api.ts](https://github.com/dinony/od-virtualscroll/blob/master/src/api.ts) reveals the current API surface.\n\n## Module Format\n\nThe lib is AoT compatible and ships with FESM5 and FESM15 exports.\n\nSee [Angular Package Format v4.0](https://docs.google.com/document/d/1CZC2rcpxffTDfRDs6p1cfbmKNLA6x5O-NtkJglDaBVs/preview) for more info.\n\nES5/UMD, ES5/ES2015 and ES2015/ES2015 exports are also provided.\n\n## Upgrade\n\n### 1.0.x -\u003e 1.1.x\n\n1.1.x uses Angular6/RxJS6.\n\n### 0.2.x -\u003e 1.x\n\nRename component input `vsScrollTop` to `vsUserCmd`.\n\n## NPM Scripts\n\n```\nnpm run {{scriptName}}\n```\n\n| Name          | Description\n|---------------|-------------------------------------------\n| buildAll      | Build lib and demo\n| cleanAll      | Remove generated directories\n| buildDemo     | Build demo bundle with AoT compilation\n| tslint        | Lint lib and demo\n| serve         | Starts browser-sync for local development\n| explore       | Source map explorer of AoT compiled demo\n\n## Contribution \u0026 Contact\n\nContribution and feedback is highly appreciated.\n\n[GitHub](https://github.com/dinony)\n\n[Twitter](https://twitter.com/dinonysaur)\n\n## License\n\nMIT\n","funding_links":[],"categories":["Uncategorized","TypeScript","Awesome Angular [![Awesome TipeIO](https://img.shields.io/badge/Awesome%20Angular-@TipeIO-6C6AE7.svg)](https://github.com/gdi2290/awesome-angular) [![Awesome devarchy.com](https://img.shields.io/badge/Awesome%20Angular-@devarchy.com-86BDC1.svg)](https://github.com/brillout/awesome-angular-components)"],"sub_categories":["Uncategorized","Angular \u003ca id=\"angular\"\u003e\u003c/a\u003e"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdinony%2Fod-virtualscroll","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdinony%2Fod-virtualscroll","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdinony%2Fod-virtualscroll/lists"}