3.8 KiB
3.8 KiB
title, impact, impactDescription, tags
| title | impact | impactDescription | tags |
|---|---|---|---|
| Use {{fn}} for Partial Application Only | LOW-MEDIUM | Clearer code, avoid unnecessary wrapping | helpers, templates, fn, partial-application |
Use {{fn}} for Partial Application Only
The {{fn}} helper is used for partial application (binding arguments), similar to JavaScript's .bind(). Only use it when you need to pre-bind arguments to a function. Don't use it to simply pass a function reference.
Incorrect (unnecessary use of {{fn}}):
// app/components/search.gjs
import Component from '@glimmer/component';
import { action } from '@ember/object';
class Search extends Component {
@action
handleSearch(event) {
console.log('Searching:', event.target.value);
}
<template>
{{! Wrong - no arguments being bound}}
<input {{on "input" (fn this.handleSearch)}} />
</template>
}
Correct (direct function reference):
// app/components/search.gjs
import Component from '@glimmer/component';
import { action } from '@ember/object';
class Search extends Component {
@action
handleSearch(event) {
console.log('Searching:', event.target.value);
}
<template>
{{! Correct - pass function directly}}
<input {{on "input" this.handleSearch}} />
</template>
}
When to Use {{fn}} - Partial Application:
Use {{fn}} when you need to pre-bind arguments to a function, similar to JavaScript's .bind():
// app/components/user-list.gjs
import Component from '@glimmer/component';
import { action } from '@ember/object';
class UserList extends Component {
@action
deleteUser(userId, event) {
console.log('Deleting user:', userId);
this.args.onDelete(userId);
}
<template>
<ul>
{{#each @users as |user|}}
<li>
{{user.name}}
{{! Correct - binding user.id as first argument}}
<button {{on "click" (fn this.deleteUser user.id)}}>
Delete
</button>
</li>
{{/each}}
</ul>
</template>
}
Multiple Arguments:
// app/components/data-grid.gjs
import Component from '@glimmer/component';
import { action } from '@ember/object';
class DataGrid extends Component {
@action
updateCell(rowId, columnKey, event) {
const newValue = event.target.value;
this.args.onUpdate(rowId, columnKey, newValue);
}
<template>
{{#each @rows as |row|}}
{{#each @columns as |column|}}
<input
value={{get row column.key}}
{{! Pre-binding rowId and columnKey}}
{{on "input" (fn this.updateCell row.id column.key)}}
/>
{{/each}}
{{/each}}
</template>
}
Think of {{fn}} like .bind():
// JavaScript comparison
const boundFn = this.deleteUser.bind(this, userId); // .bind() pre-binds args
// Template equivalent: {{fn this.deleteUser userId}}
// Direct reference
const directFn = this.handleSearch; // No pre-binding
// Template equivalent: {{this.handleSearch}}
Common Patterns:
// ❌ Wrong - no partial application
<button {{on "click" (fn this.save)}}>Save</button>
// ✅ Correct - direct reference
<button {{on "click" this.save}}>Save</button>
// ✅ Correct - partial application with argument
<button {{on "click" (fn this.save "draft")}}>Save Draft</button>
// ❌ Wrong - no partial application
<input {{on "input" (fn this.handleInput)}} />
// ✅ Correct - direct reference
<input {{on "input" this.handleInput}} />
// ✅ Correct - partial application with field name
<input {{on "input" (fn this.updateField "email")}} />
Only use {{fn}} when you're binding arguments. For simple function references, pass them directly.
Reference: Ember Templates - fn Helper