{"id":13807430,"url":"https://github.com/emberjs/ember-render-modifiers","last_synced_at":"2025-05-15T12:06:12.021Z","repository":{"id":34068421,"uuid":"167890454","full_name":"emberjs/ember-render-modifiers","owner":"emberjs","description":"Implements did-insert / did-update / will-destroy modifiers for emberjs/rfcs#415","archived":false,"fork":false,"pushed_at":"2025-03-13T13:22:22.000Z","size":950,"stargazers_count":86,"open_issues_count":18,"forks_count":33,"subscribers_count":12,"default_branch":"master","last_synced_at":"2025-05-08T09:23:25.481Z","etag":null,"topics":["hacktoberfest"],"latest_commit_sha":null,"homepage":"","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/emberjs.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","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,"zenodo":null},"funding":{"github":"emberjs","open_collective":"emberjs"}},"created_at":"2019-01-28T03:05:47.000Z","updated_at":"2025-03-13T13:22:26.000Z","dependencies_parsed_at":"2024-01-09T05:01:53.807Z","dependency_job_id":"562c7904-6f5b-4fe8-8465-26ff75eccfd3","html_url":"https://github.com/emberjs/ember-render-modifiers","commit_stats":{"total_commits":66,"total_committers":19,"mean_commits":3.473684210526316,"dds":0.6363636363636364,"last_synced_commit":"4e1fc678fb9f2aff09898eca1b68bc26ac8f0d0e"},"previous_names":[],"tags_count":11,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/emberjs%2Fember-render-modifiers","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/emberjs%2Fember-render-modifiers/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/emberjs%2Fember-render-modifiers/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/emberjs%2Fember-render-modifiers/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/emberjs","download_url":"https://codeload.github.com/emberjs/ember-render-modifiers/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253805844,"owners_count":21967054,"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":["hacktoberfest"],"created_at":"2024-08-04T01:01:25.392Z","updated_at":"2025-05-15T12:06:07.003Z","avatar_url":"https://github.com/emberjs.png","language":"JavaScript","readme":"# @ember/render-modifiers\n\nProvides element modifiers that can be used to hook into specific portions of\nthe rendering lifecycle.\n\n### When to use these modifiers (and when _not_ to use them)\n\n\u003e [!CAUTION]\n\u003e The modifiers provided in this package are ideal for quickly migrating away from\nclassic Ember components to Glimmer components, because they largely allow you to\nuse the same lifecycle hook methods you've already written. We _strongly_ encourage you to\navoid these modifiers in new code. Classic lifecycle hooks can be rewritten as custom modifiers.\n\nThe modifiers provided in this package are ideal for quickly migrating away from\nclassic Ember components to Glimmer components, because they largely allow you to\nuse the same lifecycle hook methods you've already written while attaching them to\nthese modifiers. For example, a `didInsertElement` hook could be called by\n`{{did-insert this.didInsertElement}}` to ease your migration process.\n\nHowever, we strongly encourage you to take this opportunity to rethink your\nfunctionality rather than use these modifiers as a crutch. In many cases, classic\nlifecycle hooks like `didInsertElement` can be rewritten as custom modifiers that\ninternalize functionality manipulating or generating state from a DOM element.\nOther times, you may find that a modifier is not the right fit for that logic at all,\nin which case it's worth revisiting the design to find a better pattern.\n\nEither way, we recommend using these modifiers with caution. They are very useful for\nquickly bridging the gap between classic components and Glimmer components, but they\nare still generally an anti-pattern. We recommend considering a custom modifier in\nmost use-cases where you might want to reach for this package.\n\n_**For more information on why these modifiers exist and concrete examples of what the modern alternatives to using them are,\nwatch the below talk from EmberFest 2022.**_\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://www.youtube.com/watch?v=zwewg2xmpU8\"\u003e\n    \u003cimg width=\"460\" src=\"https://user-images.githubusercontent.com/416724/195643386-e4076f35-56f6-4244-aae0-0a02f936f952.png\"\u003e\n  \u003c/a\u003e\n\u003c/p\u003e\n\n## Compatibility\n\n- Ember.js v4.12 or above\n- Ember CLI v4.12 or above\n- Node.js v18 or above\n\n## Installation\n\n```\nember install @ember/render-modifiers\n```\n\n## Usage\n\n### Example: Scrolling an element to a position\n\nThis sets the scroll position of an element, and updates it whenever the scroll\nposition changes.\n\nBefore:\n\n```hbs\n{{yield}}\n```\n\n```js\nexport default class extends Component {\n  @action\n  didRender(element) {\n    element.scrollTop = this.scrollPosition;\n  }\n}\n```\n\nAfter:\n\n```hbs\n\u003cdiv\n  {{did-insert this.setScrollPosition @scrollPosition}}\n  {{did-update this.setScrollPosition @scrollPosition}}\n  class='scroll-container'\n\u003e\n  {{yield}}\n\u003c/div\u003e\n```\n\n```js\nexport default class extends Component {\n  setScrollPosition(element, [scrollPosition]) {\n    element.scrollTop = scrollPosition;\n  }\n}\n```\n\n#### Example: Adding a class to an element after render for CSS animations\n\nThis adds a CSS class to an alert element in a conditional whenever it renders\nto fade it in, which is a bit of an extra hoop. For CSS transitions to work, we\nneed to append the element _without_ the class, then add the class after it has\nbeen appended.\n\nBefore:\n\n```hbs\n{{#if this.shouldShow}}\n  \u003cdiv class='alert'\u003e\n    {{yield}}\n  \u003c/div\u003e\n{{/if}}\n```\n\n```js\nexport default class extends Component {\n  @action\n  didRender(element) {\n    let alert = element.querySelector('.alert');\n\n    if (alert) {\n      alert.classList.add('fade-in');\n    }\n  }\n}\n```\n\nAfter:\n\n```hbs\n{{#if this.shouldShow}}\n  \u003cdiv {{did-insert this.fadeIn}} class='alert'\u003e\n    {{yield}}\n  \u003c/div\u003e\n{{/if}}\n```\n\n```js\nexport default class extends Component {\n  @action\n  fadeIn(element) {\n    element.classList.add('fade-in');\n  }\n}\n```\n\n#### Example: Resizing text area\n\nOne key thing to know about `{{did-update}}` is it will not rerun whenever the\n_contents_ or _attributes_ on the element change. For instance, `{{did-update}}`\nwill _not_ rerun when `@type` changes here:\n\n```hbs\n\u003cdiv {{did-update this.setupType}} class='{{@type}}'\u003e\u003c/div\u003e\n```\n\nIf `{{did-update}}` should rerun whenever a value changes, the value should be\npassed as a parameter to the modifier. For instance, a textarea which wants to\nresize itself to fit text whenever the text is modified could be setup like\nthis:\n\n```hbs\n\u003ctextarea {{did-update this.resizeArea @text}}\u003e\n  {{@text}}\n\u003c/textarea\u003e\n```\n\n```js\nexport default class extends Component {\n  @action\n  resizeArea(element) {\n    element.style.height = `${element.scrollHeight}px`;\n  }\n}\n```\n\n#### Example: `ember-composability-tools` style rendering\n\nThis is the type of rendering done by libraries like `ember-leaflet`, which use\ncomponents to control the _rendering_ of the library, but without any templates\nthemselves. The underlying library for this is [here](https://github.com/miguelcobain/ember-composability-tools).\nThis is a simplified example of how you could accomplish this with Glimmer\ncomponents and element modifiers.\n\nNode component:\n\n```js\n// components/node.js\nexport default class extends Component {\n  constructor() {\n    super(...arguments);\n    this.children = new Set();\n\n    this.args.parent.registerChild(this);\n  }\n\n  willDestroy() {\n    super.willDestroy(...arguments);\n\n    this.args.parent.unregisterChild(this);\n  }\n\n  registerChild(child) {\n    this.children.add(child);\n  }\n\n  unregisterChild(child) {\n    this.children.delete(child);\n  }\n\n  @action\n  didInsertNode(element) {\n    // library setup code goes here\n\n    this.children.forEach(c =\u003e c.didInsertNode(element));\n  }\n\n  @action\n  willDestroyNode(element) {\n    // library teardown code goes here\n\n    this.children.forEach(c =\u003e c.willDestroyNode(element));\n  }\n});\n```\n\n```hbs\n\u003c!-- components/node.hbs --\u003e\n{{yield (component 'node' parent=this)}}\n```\n\nRoot component:\n\n```js\n// components/root.js\nimport NodeComponent from './node.js';\n\nexport default class extends NodeComponent {}\n```\n\n```hbs\n\u003c!-- components/root.hbs --\u003e\n\u003cdiv {{did-insert this.didInsertNode}} {{will-destroy this.willDestroyNode}}\u003e\n  {{yield (component 'node' parent=this)}}\n\u003c/div\u003e\n```\n\nUsage:\n\n```hbs\n\u003cRoot as |node|\u003e\n  \u003cnode as |node|\u003e\n    \u003cnode\u003e\u003c/node\u003e\n  \u003c/node\u003e\n\u003c/Root\u003e\n```\n\n## Glint usage\nIf you are using [Glint](https://typed-ember.gitbook.io/glint/) and `environment-ember-loose`, you can add all the modifiers to your app at once by adding\n\n```ts\nimport type RenderModifiersRegistry from '@ember/render-modifiers/template-registry';\n```\nto your app's e.g. `types/glint.d.ts` file, and making sure your registry extends from RenderModifiersRegistry:\n\n```ts\ndeclare module '@glint/environment-ember-loose/registry' {\n  export default interface Registry\n    extends RenderModifiersRegistry {\n      // ...\n    }\n}\n```\n\n## Contributing\n\nSee the [Contributing](CONTRIBUTING.md) guide for details.\n\n## License\n\nThis project is licensed under the [MIT License](LICENSE.md).\n","funding_links":["https://github.com/sponsors/emberjs","https://opencollective.com/emberjs"],"categories":["Packages"],"sub_categories":["Modifiers"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Femberjs%2Fember-render-modifiers","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Femberjs%2Fember-render-modifiers","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Femberjs%2Fember-render-modifiers/lists"}