{"id":15015843,"url":"https://github.com/adopted-ember-addons/ember-impagination","last_synced_at":"2025-04-09T15:07:52.356Z","repository":{"id":43873961,"uuid":"44565307","full_name":"adopted-ember-addons/ember-impagination","owner":"adopted-ember-addons","description":"An Ember Addon that puts the fun back in asynchronous, paginated datasets","archived":false,"fork":false,"pushed_at":"2023-04-17T10:56:42.000Z","size":1850,"stargazers_count":122,"open_issues_count":28,"forks_count":14,"subscribers_count":14,"default_branch":"master","last_synced_at":"2025-04-09T15:07:45.023Z","etag":null,"topics":["dataset","ember","ember-addon","infinite-scroll","pagination"],"latest_commit_sha":null,"homepage":"https://ember-impagination.netlify.com/","language":"JavaScript","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/adopted-ember-addons.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2015-10-19T21:30:49.000Z","updated_at":"2024-05-30T02:10:56.000Z","dependencies_parsed_at":"2024-06-21T16:35:15.112Z","dependency_job_id":"5c91e3b5-fd79-49ec-89d7-626c63c159ce","html_url":"https://github.com/adopted-ember-addons/ember-impagination","commit_stats":{"total_commits":270,"total_committers":18,"mean_commits":15.0,"dds":0.6592592592592592,"last_synced_commit":"c86ff56b124afe7406e096cd20a5e549161bdc03"},"previous_names":[],"tags_count":10,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adopted-ember-addons%2Fember-impagination","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adopted-ember-addons%2Fember-impagination/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adopted-ember-addons%2Fember-impagination/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adopted-ember-addons%2Fember-impagination/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/adopted-ember-addons","download_url":"https://codeload.github.com/adopted-ember-addons/ember-impagination/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248055284,"owners_count":21040157,"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":["dataset","ember","ember-addon","infinite-scroll","pagination"],"created_at":"2024-09-24T19:48:02.613Z","updated_at":"2025-04-09T15:07:52.326Z","avatar_url":"https://github.com/adopted-ember-addons.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Ember-Impagination\n\n[![npm version](https://badge.fury.io/js/ember-impagination.svg)](https://badge.fury.io/js/ember-impagination)\n[![Ember Observer Score](http://emberobserver.com/badges/ember-impagination.svg)](http://emberobserver.com/addons/ember-impagination)\n[![Build Status](https://travis-ci.org/adopted-ember-addons/ember-impagination.svg?branch=master)](https://travis-ci.org/adopted-ember-addons/ember-impagination)\n[![Netlify Status](https://api.netlify.com/api/v1/badges/f31e187a-b0aa-4b4f-9567-9bcb6873f0a5/deploy-status)](https://app.netlify.com/sites/ember-impagination/deploys)\n\n_Ember-Impagination_ is an Ember binding for\n[Impagination](https://github.com/flexyford/impagination), a front-end\ndata-layer for the paginated API on your server. _Ember-Impagination_\nleverages the power of Glimmer and provides your component the data it\nneeds to render quickly each and every time.\n\n\u003e Impagination README:\n\u003e Whatever your use-case: infinite scrolling lists, a carousel\n\u003e browser, or even a classic page-by-page result list, Impagination\n\u003e frees you to focus on what you want to do with your data, not the\n\u003e micro-logistics of when to fetch it. All you provide Impagination is\n\u003e the logic to fetch a single page, plus how many pages you want it to\n\u003e pre-fetch ahead of you, and it will figure out the rest.\n\u003e Impagination is built using an event-driven immutable style, so it\n\u003e is ideal for use with UI frameworks like Ember . . .\n\nHence, we present _Ember-Impagination_.\n\n_Ember-Impagination_ provides you with a component,\n`{{impagination-dataset as |data|}}`, you can use to feed data into\nyour templates while having that data look exactly like an\n`Ember.Array`.\n\n\u003e Note: _Ember-Impagination_ does not provide any of the visual\n\u003e elements in a system like infinite scroll. You'll still need to use\n\u003e a special component like `virtual-each` or\n\u003e `ember-collection`. Instead, _Ember-Impagination_ simplifies feeding\n\u003e your components fetched data.\n\n## Installation\n\n- `ember install ember-impagination`\n\n## Demo\n\n[Ember-Impagination Demo](https://ember-impagination.netlify.com/)\n\nThe demo presents a finite scroll implementation of\n_Ember-Impagination_. It scrolls through the ROYGBIV color spectrum by\nloading and unloading pages of records, where each record is a unique\ncolor-hue. At the top of the demo, you will find a visualization for\npages. Resolved (Loaded) Pages are green, Pending (Loading) pages are\nwhite, and Unrequested (Unloaded) pages are black. The white-bar\nrepresents the top-most index of the scroll view.\n\n![ember-impagination](http://g.recordit.co/iltQTaYwSb.gif)\n\nThe demo is implemented using\n[virtual-each](https://github.com/jasonmit/virtual-each) due to the\nsimplicity of the component. However, _Ember-Impagination_ can also be\nutilized with other components like\n[ember-collection](https://github.com/emberjs/ember-collection), or\neven a simple `{{each}}`. By design, _Ember-Impagination_ leverages\nGlimmer and yields paginated data from your server's API to components\nwhich expect an array.\n\n## Usage\n\n### Impagination-Dataset Component\n\nTo create an `impagination-dataset` there are two _required_\nparameters, `fetch` and `page-size`. Optional parameters include\n`load-horizon`, `unload-horizon`, `unfetch`. See\n[Impagination](https://github.com/flexyford/impagination) for detailed\nattribute descriptions.\n\n```hbs\n{{!-- app/templates/index.hbs --}}\n{{#impagination-dataset\n  fetch=fetch\n  page-size=pageSize\n  load-horizon=loadHorizon\n  as |records|}}\n  \u003cdiv class=\"records\"\u003eTotal Records: {{records.length}}\u003c/div\u003e\n  {{#each records as |record|}}\n    \u003cdiv class=\"record\"\u003eRecord {{record.content.id}}\u003c/div\u003e\n  {{/each}}\n{{/impagination-dataset}}\n```\n\nNow, in your route, you can define the actual `(un)fetch` functions\nthat tell `{{impagination-dataset}}` how it should request each\nindividual page, and the `(un)loadHorizon` which specify how many\npages to request ahead/behind.\n\n```javascript\n// app/route/record.js\nexport default Ember.Route.extend({\n  pageSize: 5, // fetch records in pages of 5 (*required*)\n  loadHorizon: 10, // fetch  records \"inclusive\" (+/- loadHorizon)   of the current readOffset (default: pageSize)\n  //unloadHorizon: Infinity, // unload records \"exclusive\" (+/- unloadHorizon) of the current readOffset (default: Infinity)\n  //readOffset: 0,           // the initial readOffset of the dataset (default: 0)\n\n  // fetch() function is invoked whenever a page is requested within the loadHorizon\n  fetch: function(pageOffset, pageSize, stats) {\n    // function which returns a \"thenable\" (*required*)\n    let params = {\n      page: pageOffset\n    };\n    // fetch a page of records at the pageOffset\n    return this.store.query(\"record\", params).then(data =\u003e {\n      let meta = data.get(\"meta\");\n      stats.totalPages = meta.totalPages;\n      return data.toArray();\n    });\n  },\n  // unfetch() function is invoked whenever a page is unloaded\n  unfetch: function(records, pageOffset) {\n    this.store.findByIds(\n      \"record\",\n      records\n        .map(r =\u003e r.id)\n        .then(function(records) {\n          records.forEach(record =\u003e record.deleteRecord());\n        })\n    );\n  }\n});\n```\n\nThis setup will immediatly call fetch twice (for records 0-4 [page 0]\nand records 5-9 [page 1])\n\n```text\n\nTotal Records: 10\nRecord 0\nRecord 1\nRecord 2\n...\nRecord 9\n```\n\n#### Passing the Fetch Function\n\nIn **Ember 1.13 and above**, we can use closure-actions to pass the\nfetch function into `ember-impagination`\n\n```handlebars\n{{#impagination-dataset fetch=(action \"fetch\")}}\n```\n\n```javascript\n// app/route/record.js\nexport default Ember.Route.extend({\n  // fetch() function is invoked whenever a page is requested within the loadHorizon\n  actions: {\n    fetch(pageOffset, pageSize, stats) {\n      // function which returns a \"thenable\" (*required*)\n      let params = {\n        query: query\n      };\n      // fetch a page of records at the pageOffset\n      return this.store.query(\"record\", params).then(data =\u003e {\n        let meta = data.get(\"meta\");\n        stats.totalPages = meta.totalPages;\n        return data.toArray();\n      });\n    }\n  }\n});\n```\n\n\u003e We do not recommend defining `fetch` inside your controller because\n\u003e it requires\n\u003e [injecting the store into the controller](https://github.com/adopted-ember-addons/ember-impagination/issues/39#issuecomment-172101680)\n\nIn **Ember 1.12 and below** we cannot define `fetch` in our actions\nhash. We must instead bind it to our controller.\n\n```handlebars\n{{#impagination-dataset fetch=fetch)}}\n```\n\n```javascript\n// app/route/record.js\nexport default Ember.Route.extend({\n    fetch: function(pageOffset, pageSize, stats) {\n      return this.store.query(...);\n    },\n    setupController: function(controller, model){\n      this._super.apply(this, arguments);\n      controller.set('fetch', this.fetch.bind(this));\n    }\n});\n```\n\n### Filtering Records\n\nWe fetch records using an immutable style, but we often require\nfiltering by mutable records in our dataset. To enable filtering, pass\na filter `callback` to `ember-impagination` as you would to\n`Array.prototype.filter()`. The filters are applied as soon as a page\nis resolved. To filter a page at other times in your application see\n[`refilter`](#dataset-actions).\n\n```handlebars\n{{#impagination-dataset fetch=(action \"fetch\") filter=filterCallback}}\n```\n\n```javascript\n// app/route/record.js\nexport default Ember.Route.extend({\n  // filter() function is invoked whenever a page is resolved or refiltered\n  filterCallback(record /*, index, records*/) {\n    // function which rejects deleted records\n    return !record.get(\"isDeleted\");\n  }\n});\n```\n\n### Dataset API\n\nThere are a number actions to update the dataset.\n\n#### Updating the Dataset\n\n| Actions       |    Parameters    | Description                                                                                                    |\n| ------------- | :--------------: | :------------------------------------------------------------------------------------------------------------- |\n| refilter      | [filterCallback] | Reapplies the filter for all resolved pages. If `filterCallback` is provided, applies and sets the new filter. |\n| reset         |     [offset]     | Unfetches all pages and clears the `state`. If `offset` is provided, fetches records starting at `offset`.     |\n| setReadOffset |     [offset]     | Sets the `readOffset` and fetches records resuming at `offset`                                                 |\n| ~~reload~~    |   ~~[index]~~    | _Removed in `1.0.0` release. Please use `reset` instead._                                                      |\n\n#### Updating the State\n\n| Actions | Parameters  | Defaults                 | Description                            |\n| ------- | :---------: | :----------------------- | :------------------------------------- |\n| post    | data, index | index = 0                | Creates record with `data` at `index`. |\n| put     | data, index | index = state.readOffset | Merges `data` into record at `index`.  |\n| delete  |    index    | index= state.readOffset  | Deletes record at `index`.             |\n\nThese functions can be called from the route/controller or from child\ncomponents in the handlebars templates. In the examples below, we\n`reset` the dataset upon search queries through the {{search-pane}}\ncomponent using both options.\n\n#### resetting from the parent route\n\nIn order to call dataset actions from the route, we will have to\nobserve the latest dataset and dataset-actions with the `on-observe`\nparameter.\n\n```handlebars\n{{#search-pane search=(action \"search\")}}\n  {{#impagination-dataset on-observe=(action \"observeDataset\") fetch=(action \"fetch\") as |dataset|}}\n    {{#ember-collection items=dataset as |record|}}\n      {{chat-search-result result=record}}\n    {{/ember-collection}}\n  {{/impagination-dataset}}\n{{/search-pane}}\n```\n\n```javascript\n_resetDataset() {\n  this.get('dataset').reset();\n},\n\nactions: {\n  observeDataset: function(dataset) {\n    this.set('dataset', dataset);\n  },\n  search(query) {\n    this.set('searchParams', query);\n    this._resetDataset();\n  },\n  fetch(pageOffset, pageSize, stats) {\n    params = this.get('params');\n    return this.store.query('records', params);\n  }\n}\n```\n\n#### resetting from child components\n\nHere we do not need to utilize `impagination-dataset`'s `on-observe`\nparameter. The `reset` action is simply called by a child component.\n\n```handlebars\n{{#impagination-dataset fetch=(action \"fetch\") as |dataset|}}\n  {{!-- reset dataset and start fetching at record index 0 --}}\n  {{#search-pane search=(action \"search\") on-search-results=(action dataset.reset 0)}}\n      {{#ember-collection items=dataset as |record|}}\n        {{chat-search-result result=record}}\n      {{/ember-collection}}\n  {{/search-pane}}\n{{/impagination-dataset}}\n```\n\n### Create your own Dataset\n\nIf `{{impagination-dataset}}` is not an ideal component for your\nunique `Impagination` needs, you can get into the nitty gritty, and\nuse `Impagination` directly. If you find yourself creating your own\n`Dataset`, let us know how you are using `Dataset` and\n`Impagination`. It may be a reason for improvements or another ember\naddon.\n\n```js\nimport Dataset from 'impagination/dataset';\n\nlet dataset = new Dataset({\n  pageSize: 5,\n  fetch: function(pageOffset, pageSize, stats) {\n    return new Ember.RSVP.Promise((resolve)=\u003e {\n      let data = // Array\n      resolve(data);\n    },\n\n    return this.store.query('record', params).then((data) =\u003e {\n      let meta = result.get('meta');\n      stats.totalPages = meta.totalPages;\n      return result.toArray();\n    });\n  },\n  observe: (state) =\u003e {}\n});\n```\n\n### Running tests\n\n- `ember test` – Runs the test suite on the current Ember version\n- `ember test --server` – Runs the test suite in \"watch mode\"\n- `ember try:each` – Runs the test suite against multiple Ember versions\n\n### Running the dummy application\n\n- `ember serve`\n- Visit the dummy application at [http://localhost:4200](http://localhost:4200).\n\nFor more information on using ember-cli, visit [https://ember-cli.com/](https://ember-cli.com/).\n\n### Code of Conduct\n\nPlease note that this project is released with a Contributor Code of\nConduct. By participating in this project you agree to abide by its\nterms, which can be found in the `CODE_OF_CONDUCT.md` file in this\nrepository.\n\n### License\n\n---\n\nThis project is licensed under the [MIT License](LICENSE.md).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fadopted-ember-addons%2Fember-impagination","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fadopted-ember-addons%2Fember-impagination","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fadopted-ember-addons%2Fember-impagination/lists"}