Files
marco/.agents/skills/ember-best-practices/rules/advanced-modifiers.md

2.9 KiB

title, impact, impactDescription, tags
title impact impactDescription tags
Use Modifiers for DOM Side Effects LOW-MEDIUM Better separation of concerns modifiers, dom, lifecycle, advanced

Use Modifiers for DOM Side Effects

Use modifiers (element modifiers) to handle DOM side effects and lifecycle events in a reusable, composable way.

Incorrect (manual DOM manipulation in component):

// app/components/chart.gjs
import Component from '@glimmer/component';

class Chart extends Component {
  chartInstance = null;

  constructor() {
    super(...arguments);
    // Can't access element here - element doesn't exist yet!
  }

  willDestroy() {
    super.willDestroy();
    this.chartInstance?.destroy();
  }

  <template>
    <canvas id="chart-canvas"></canvas>
    {{! Manual setup is error-prone and not reusable }}
  </template>
}

Correct (function modifier - preferred for simple side effects):

// app/modifiers/chart.js
import { modifier } from 'ember-modifier';

export default modifier((element, [config]) => {
  // Initialize chart
  const chartInstance = new Chart(element, config);

  // Return cleanup function
  return () => {
    chartInstance.destroy();
  };
});

Also correct (class-based modifier for complex state):

// app/modifiers/chart.js
import Modifier from 'ember-modifier';
import { registerDestructor } from '@ember/destroyable';

export default class ChartModifier extends Modifier {
  chartInstance = null;

  modify(element, [config]) {
    // Cleanup previous instance if config changed
    if (this.chartInstance) {
      this.chartInstance.destroy();
    }

    this.chartInstance = new Chart(element, config);

    // Register cleanup
    registerDestructor(this, () => {
      this.chartInstance?.destroy();
    });
  }
}
// app/components/chart.gjs
import chart from '../modifiers/chart';

<template>
  <canvas {{chart @config}}></canvas>
</template>

Use function modifiers for simple side effects. Use class-based modifiers only when you need complex state management.

For commonly needed modifiers, use ember-modifier helpers:

// app/modifiers/autofocus.js
import { modifier } from 'ember-modifier';

export default modifier((element) => {
  element.focus();
});
// app/components/input-field.gjs
import autofocus from '../modifiers/autofocus';

<template><input {{autofocus}} type="text" /></template>

Use ember-resize-observer-modifier for resize handling:

ember install ember-resize-observer-modifier
// app/components/resizable.gjs
import onResize from 'ember-resize-observer-modifier';

<template>
  <div {{onResize this.handleResize}}>
    Content that responds to size changes
  </div>
</template>

Modifiers provide a clean, reusable way to manage DOM side effects without coupling to specific components.

Reference: Ember Modifiers