130 lines
2.9 KiB
Markdown
130 lines
2.9 KiB
Markdown
---
|
|
title: Use Modifiers for DOM Side Effects
|
|
impact: LOW-MEDIUM
|
|
impactDescription: Better separation of concerns
|
|
tags: 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):**
|
|
|
|
```glimmer-js
|
|
// 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):**
|
|
|
|
```javascript
|
|
// 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):**
|
|
|
|
```javascript
|
|
// 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();
|
|
});
|
|
}
|
|
}
|
|
```
|
|
|
|
```glimmer-js
|
|
// 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:**
|
|
|
|
```javascript
|
|
// app/modifiers/autofocus.js
|
|
import { modifier } from 'ember-modifier';
|
|
|
|
export default modifier((element) => {
|
|
element.focus();
|
|
});
|
|
```
|
|
|
|
```glimmer-js
|
|
// app/components/input-field.gjs
|
|
import autofocus from '../modifiers/autofocus';
|
|
|
|
<template><input {{autofocus}} type="text" /></template>
|
|
```
|
|
|
|
**Use ember-resize-observer-modifier for resize handling:**
|
|
|
|
```bash
|
|
ember install ember-resize-observer-modifier
|
|
```
|
|
|
|
```glimmer-js
|
|
// 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](https://guides.emberjs.com/release/components/template-lifecycle-dom-and-modifiers/)
|