Skip to content
Implements did-insert / did-update / will-destroy modifiers for emberjs/rfcs#415
Branch: master
Clone or download
rwjblue Merge pull request #8 from GavinJoyce/gj/api-docs
`{{did-insert}}`, `{{did-update}}` and `{{will-destroy}}` API docs
Latest commit 48faeb4 Mar 13, 2019
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
addon/modifiers
app/modifiers
config eslint . --fix Jan 28, 2019
tests
vendor Initial Commit from Ember CLI v3.7.1 Jan 28, 2019
.editorconfig Initial Commit from Ember CLI v3.7.1 Jan 28, 2019
.ember-cli Initial Commit from Ember CLI v3.7.1 Jan 28, 2019
.eslintignore Initial Commit from Ember CLI v3.7.1 Jan 28, 2019
.eslintrc.js Setup prettier! Jan 28, 2019
.gitignore
.npmignore Initial Commit from Ember CLI v3.7.1 Jan 28, 2019
.prettierrc.js
.template-lintrc.js eslint . --fix Jan 28, 2019
.travis.yml
.watchmanconfig
CHANGELOG.md
CONTRIBUTING.md Initial Commit from Ember CLI v3.7.1 Jan 28, 2019
LICENSE.md
README.md
ember-cli-build.js Initial Commit from Ember CLI v3.7.1 Jan 28, 2019
index.js eslint . --fix Jan 28, 2019
package.json
testem.js
yarn.lock Add changelog generation setup and initial release version. Jan 29, 2019

README.md

@ember/render-modifiers

Provides element modifiers that can be used to hook into specific portions of the rendering lifecycle.

Compatibility

  • Ember.js v2.18 or above
  • Ember CLI v2.13 or above

Installation

ember install @ember/render-modifiers

Usage Examples

Example: Scrolling an element to a position

This sets the scroll position of an element, and updates it whenever the scroll position changes.

Before:

{{yield}}
export default Component.extend({
  classNames: ['scroll-container'],

  didRender() {
    this.element.scrollTop = this.scrollPosition;
  }
});

After:

<div
  {{did-insert this.setScrollPosition @scrollPosition}}
  {{did-update this.setScrollPosition @scrollPosition}}

  class="scroll-container"
>
  {{yield}}
</div>
export default class Component.extend({
  setScrollPosition(element, [scrollPosition]) {
    element.scrollTop = scrollPosition;
  }
})

Example: Adding a class to an element after render for CSS animations

This adds a CSS class to an alert element in a conditional whenever it renders to fade it in, which is a bit of an extra hoop. For CSS transitions to work, we need to append the element without the class, then add the class after it has been appended.

Before:

{{#if this.shouldShow}}
  <div class="alert">
    {{yield}}
  </div>
{{/if}}
export default Component.extend({
  didRender() {
    let alert = this.element.querySelector('.alert');

    if (alert) {
      alert.classList.add('fade-in');
    }
  }
});

After:

{{#if this.shouldShow}}
  <div {{did-insert this.fadeIn}} class="alert">
    {{yield}}
  </div>
{{/if}}
export default Component.extend({
  fadeIn(element) {
    element.classList.add('fade-in');
  }
});

Example: Resizing text area

One key thing to know about {{did-update}} is it will not rerun whenever the contents or attributes on the element change. For instance, {{did-update}} will not rerun when @type changes here:

<div {{did-update this.setupType}} class="{{@type}}"></div>

If {{did-update}} should rerun whenever a value changes, the value should be passed as a parameter to the modifier. For instance, a textarea which wants to resize itself to fit text whenever the text is modified could be setup like this:

<textarea {{did-update this.resizeArea @text}}>
  {{@text}}
</textarea>
export default Component.extend({
  resizeArea(element) {
    element.style.height = `${element.scrollHeight}px`;
  }
});

Example: ember-composability-tools style rendering

This is the type of rendering done by libraries like ember-leaflet, which use components to control the rendering of the library, but without any templates themselves. The underlying library for this is here. This is a simplified example of how you could accomplish this with Glimmer components and element modifiers.

Node component:

// components/node.js
export default Component.extend({
  init() {
    super(...arguments);
    this.children = new Set();

    this.parent.registerChild(this);
  }

  willDestroy() {
    super(...arguments);

    this.parent.unregisterChild(this);
  }

  registerChild(child) {
    this.children.add(child);
  }

  unregisterChild(child) {
    this.children.delete(child);
  }

  didInsertNode(element) {
    // library setup code goes here

    this.children.forEach(c => c.didInsertNode(element));
  }

  willDestroyNode(element) {
    // library teardown code goes here

    this.children.forEach(c => c.willDestroyNode(element));
  }
}
<!-- components/node.hbs -->
{{yield (component "node" parent=this)}}

Root component:

// components/root.js
import NodeComponent from './node.js';

export default NodeComponent.extend();
<!-- components/root.hbs -->
<div
  {{did-insert (action this.didInsertNode)}}
  {{will-destroy (action this.willDestroyNode)}}
>
  {{yield (component "node" parent=this)}}
</div>

Usage:

<Root as |node|>
  <node as |node|>
    <node />
  </node>
</Root>

License

This project is licensed under the MIT License.

You can’t perform that action at this time.