{"id":15538894,"url":"https://github.com/buschtoens/ember-route-task-helper","last_synced_at":"2025-04-14T15:54:38.897Z","repository":{"id":40414483,"uuid":"99274092","full_name":"buschtoens/ember-route-task-helper","owner":"buschtoens","description":"Easily access tasks defined on a route in your templates","archived":false,"fork":false,"pushed_at":"2023-12-15T20:24:19.000Z","size":1508,"stargazers_count":27,"open_issues_count":26,"forks_count":4,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-04-08T20:07:55.772Z","etag":null,"topics":["ember","ember-addon","ember-concurrency","ember-helpers"],"latest_commit_sha":null,"homepage":"https://buschtoens.github.io/ember-route-task-helper/","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/buschtoens.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"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":"2017-08-03T20:52:26.000Z","updated_at":"2024-05-30T02:44:19.000Z","dependencies_parsed_at":"2024-06-21T17:52:58.842Z","dependency_job_id":"902cac8b-0fb6-44fe-bb66-36eff87c65aa","html_url":"https://github.com/buschtoens/ember-route-task-helper","commit_stats":{"total_commits":219,"total_committers":8,"mean_commits":27.375,"dds":0.5068493150684932,"last_synced_commit":"2ff6c9838d7e18392f844d452912f47b2a41c8cf"},"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/buschtoens%2Fember-route-task-helper","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/buschtoens%2Fember-route-task-helper/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/buschtoens%2Fember-route-task-helper/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/buschtoens%2Fember-route-task-helper/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/buschtoens","download_url":"https://codeload.github.com/buschtoens/ember-route-task-helper/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248912042,"owners_count":21182202,"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":["ember","ember-addon","ember-concurrency","ember-helpers"],"created_at":"2024-10-02T12:07:30.714Z","updated_at":"2025-04-14T15:54:38.868Z","avatar_url":"https://github.com/buschtoens.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ember-route-task-helper\n\n[![Build Status](https://travis-ci.org/buschtoens/ember-route-task-helper.svg)](https://travis-ci.org/buschtoens/ember-route-task-helper)\n[![npm version](https://badge.fury.io/js/ember-route-task-helper.svg)](http://badge.fury.io/js/ember-route-task-helper)\n[![Download Total](https://img.shields.io/npm/dt/ember-route-task-helper.svg)](http://badge.fury.io/js/ember-route-task-helper)\n[![Ember Observer Score](https://emberobserver.com/badges/ember-route-task-helper.svg)](https://emberobserver.com/addons/ember-route-task-helper)\n[![code style: prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg)](https://github.com/prettier/prettier)  \n[![Dependabot enabled](https://img.shields.io/badge/dependabot-enabled-blue.svg?logo=dependabot)](https://dependabot.com/)\n[![dependencies Status](https://david-dm.org/buschtoens/ember-route-task-helper/status.svg)](https://david-dm.org/buschtoens/ember-route-task-helper)\n[![devDependencies Status](https://david-dm.org/buschtoens/ember-route-task-helper/dev-status.svg)](https://david-dm.org/buschtoens/ember-route-task-helper?type=dev)\n\nThe `route-task` template helper allows you to easily access tasks defined on a\nroute in the currently active route hierarchy. Essentially this addon is just\nlike [**ember-route-action-helper**][ember-route-action-helper] but for\n[**ember-concurrency**][ember-concurrency] tasks.\n\n## Installation\n\n```\nember install ember-route-task-helper\n```\n\nThis addon will work on Ember versions `2.4.x` and up only, due to use of the\nnew `RouterService`. If your Ember version does not natively support it yet, you\nneed to install the polyfill:\n\n```\nember install ember-router-service-polyfill\n```\n\nOf course, you need to have [**ember-concurrency**][ember-concurrency]\ninstalled. If you haven't already, run this command first:\n\n```\nember install ember-concurrency\n```\n\nMinimum required version is `0.6.x`.\n\nIf you want to use this addon inside an engine, you'll need to have an\n`EngineRouterService`. You can use my PoC linked in\n[ember-engines#587][engine-router-service] until it's released officially as\npart of ember-engines.\n\n[engine-router-service]: https://github.com/ember-engines/ember-engines/issues/587#issue-355568777\n\n## Usage\n\n### Template Helper: `(route-task taskName ...curryArguments)`\n\nWherever in a template you would access a task by its name, replace it with\n`(route-task \"taskName\")` and move that task to a route. For instance:\n\n```hbs\n{{task-button\n  task=myTask\n}}\n\n{{!-- now becomes --}}\n\n{{task-button\n  task=(route-task \"myTask\")\n}}\n```\n\nNotice the quotes around `\"myTask\"`.\n\nYou can also curry your tasks:\n\n```hbs\n{{task-button\n  task=(task myTask \"Freddie\" \"Morecurry\")\n}}\n\n{{!-- now becomes --}}\n\n{{task-button\n  task=(route-task \"myTask\" \"Freddie\" \"Morecurry\")\n}}\n```\n\n#### Exemplary Migration\n\nLet's start with a traditional task defined on a component.\n\n```js\n// app/components/delete-user.js\nimport Component from '@ember/component';\nimport { get } from '@ember/object';\nimport { task, timeout } from 'ember-concurrency';\n\nexport default Component.extends({\n  /**\n   * A User record.\n   * @type {DS.Model}\n   */\n  user: null,\n\n  /**\n   * Deletes the user after a timeout of 5 seconds.\n   * @type {Task}\n   */\n  deleteUser: task(function*() {\n    timeout(5000); // give the user time to think about it\n    yield get(this, 'user').destroyRecord();\n  }).drop()\n});\n```\n\n```hbs\n{{!-- app/templates/components/delete-user.hbs --}}\n\n{{#if deleteUser.isIdle}}\n  \u003cbutton onclick={{perform deleteUser}}\u003e\n    Delete {{user.name}}\n  \u003c/button\u003e\n{{else}}\n  Deleting {{user.name}} in 5 seconds. You can still abort.\n\n  \u003cbutton onclick={{cancel-all deleteUser}}\u003e\n    Nah, spare their life.\n  \u003c/button\u003e\n{{/if}}\n```\n\nAnd now we'll move it to a route, shall we?\n\n```js\n// app/routes/user.js\nimport Route from '@ember/routing/route';\nimport { get } from '@ember/object';\nimport { task, timeout } from 'ember-concurrency';\n\nexport default Route.extends({\n  /**\n   * Deletes the current model after a timeout of 5 seconds.\n   * @type {Task}\n   */\n  deleteUser: task(function*() {\n    timeout(5000); // give the user time to think about it\n    const user = this.modelFor(this.routeName);\n    yield user.destroyRecord();\n  }).drop()\n});\n```\n\n```hbs\n{{!-- app/templates/user.hbs --}}\n\n{{#if (get (route-task \"deleteUser\") \"isIdle\")}}\n  \u003cbutton onclick={{perform (route-task \"deleteUser\")}}\u003e\n    Delete {{model.name}}\n  \u003c/button\u003e\n{{else}}\n  Deleting {{model.name}} in 5 seconds. You can still abort.\n\n  \u003cbutton onclick={{cancel-all (route-task \"deleteUser\")}}\u003e\n    Nah, spare their life.\n  \u003c/button\u003e\n{{/if}}\n```\n\n### Currying\n\n```js\n// app/routes/user.js\nimport Route from '@ember/routing/route';\nimport { get } from '@ember/object';\nimport { task, timeout } from 'ember-concurrency';\n\nexport default Route.extends({\n  /**\n   * Deletes the current model after a timeout of 5 seconds.\n   * @type {Task}\n   */\n  deleteUser: task(function*(user) {\n    timeout(5000); // give the user time to think about it\n    yield user.destroyRecord();\n  }).drop()\n});\n```\n\n```hbs\n{{!-- app/templates/user.hbs --}}\n\n{{task-button\n  task=(route-task \"deleteUser\" model)\n  idleLabel=(concat \"Delete \" model.name)\n  runningLabel=\"Cancel deletion\"\n}}\n```\n\n### Notes on Syntax\n\nI personally dislike repeating `(route-task)` a bunch of times in my templates or even worse having to use the `(get)` helper to derive state from a task. You can avoid that by either only passing a task to a component (as shown in the `{{task-button}}` example above) or by using the `{{with}}` helper:\n\n```hbs\n{{#with (route-task \"deleteUser\" model) as |deleteUser|}}\n  {{#if deleteUser.isIdle}}\n    \u003cbutton onclick={{perform deleteUser}}\u003e\n      Delete {{model.name}}\n    \u003c/button\u003e\n  {{else}}\n    Deleting {{model.name}} in 5 seconds. You can still abort.\n\n    \u003cbutton onclick={{cancel-all deleteUser}}\u003e\n      Nah, spare their life.\n    \u003c/button\u003e\n  {{/if}}\n{{/with}}\n```\n\n## Util: `routeTask(context, taskName, ...curryArguments)`\n\nThere also is a `routeTask` util, that's really similar to [**ember-invoke-action**][ember-invoke-action] and might come in handy for JS heavy components.\n\n```js\n// app/components/delete-user.js\nimport Component from '@ember/component';\nimport { get } from '@ember/object';\nimport { routeTask } from 'ember-route-task-helper';\n\nexport default Component.extends({\n  /**\n   * A User record.\n   * @type {DS.Model}\n   */\n  user: null,\n\n  /**\n   * Deletes the user after a timeout of 5 seconds.\n   */\n  click() {\n    const user = get(this, 'user');\n    routeTask(this, 'deleteUser').perform(user);\n  }\n});\n```\n\n### Currying\n\nAs with the `(route-task)` helper, you can curry the task with as many arguments as you like. So the above is interchangeable with:\n\n```js\nclick() {\n  const user = get(this, 'user');\n  routeTask(this, 'deleteUser', user).perform();\n}\n```\n\n### `routeTaskFromRouter(router, taskName, ...curryArguments)`\n\nInternally `routeTask` performs a lookup for the `Router` everytime you call it. If you already happen to have the router instance available in your current scope, you could also pass it directly to skip the lookup:\n\n```js\n// app/components/delete-user.js\nimport Component from '@ember/component';\nimport { get, computed } from '@ember/object';\nimport { getOwner } from '@ember/application';\nimport { routeTaskFromRouter } from 'ember-route-task-helper';\n\nexport default Component.extends({\n  /**\n   * A User record.\n   * @type {DS.Model}\n   */\n  user: null,\n\n  /**\n   * The app's router instance.\n   * @type {Ember.Router}\n   */\n  router: computed(function() {\n    return getOwner(this).lookup('main:router');\n  }).readOnly(),\n\n  /**\n   * Deletes the user after a timeout of 5 seconds.\n   */\n  click() {\n    const router = get(this, 'router');\n    const user = get(this, 'user');\n    routeTaskFromRouter(router, 'deleteUser').perform(user);\n  }\n});\n```\n\n### Notes on DDAU\n\nIn my opinion, using `routeTask` in components _generally_ isn't a good design pattern. I would much rather prefer to explicitly pass the route task as an attribute:\n\n```hbs\n{{my-component taskName=(route-task \"taskName\")}}\n```\n\nJust by looking at the component invocation in the template, the user should be able to judge what's going in and what's coming out of a component (DDAU). This way components remain completely agnostic and make no assumptions about the environment they are invoked in.\n\nCalling `routeTask` inside a component is really non-transparent and promotes an unhealthy invisible entanglement.\n\nOn the other hand, you can already call `(route-task)` in the component's template.\n\nI've implemented it for feature parity. But just because it's there, doesn't mean you _have_ to use it. But don't let me stop you. :stuck_out_tongue_winking_eye:\n\n## Contributing\n\nI sincerely hope this addon serves you well. Should you encounter a bug, have a great idea or just a question, please do [open an issue][new-issue] and let me know. Even better yet, submit a PR yourself! :blush:\n\nThis addon is using the [Prettier code formatter][prettier]. It's embedded as a fixable eslint rule. If you're editor is set up to fix eslint errors on save, you're code is auto formatted. If not, please make sure you're not getting any linter errors.\n\n## Attribution\n\nThe original idea for this addon was brought up in [machty/ember-concurrency#89][issue-89] by [@Luiz-N][luiz-n]. This addon is in many ways a straight copy and paste from [ember-route-action-helper][ember-route-action-helper] by [@DockYard][dockyard].\n\nA huge **thank you** to goes out to [@machty][machty] for developing [ember-concurrency][ember-concurrency] in the first place. :heart:\n\n[ember-concurrency]: https://github.com/machty/ember-concurrency\n[ember-route-action-helper]: https://github.com/DockYard/ember-route-action-helper\n[ember-invoke-action]: https://github.com/martndemus/ember-invoke-action\n[issue-89]: https://github.com/machty/ember-concurrency/issues/89\n[luiz-n]: https://github.com/Luiz-N\n[dockyard]: https://github.com/DockYard\n[machty]: https://github.com/machty\n[new-issue]: https://github.com/buschtoens/ember-route-task-helper/issues/new\n[prettier]: https://prettier.io/\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbuschtoens%2Fember-route-task-helper","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbuschtoens%2Fember-route-task-helper","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbuschtoens%2Fember-route-task-helper/lists"}