Add ember-best-practices skill
This commit is contained in:
129
.agents/skills/ember-best-practices/rules/advanced-modifiers.md
Normal file
129
.agents/skills/ember-best-practices/rules/advanced-modifiers.md
Normal file
@@ -0,0 +1,129 @@
|
||||
---
|
||||
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/)
|
||||
Reference in New Issue
Block a user