Skip to content

Latest commit

 

History

History
99 lines (74 loc) · 3.19 KB

no-builtin-form-components.md

File metadata and controls

99 lines (74 loc) · 3.19 KB

no-builtin-form-components

✅ The extends: 'recommended' property in a configuration file enables this rule.

Ember's built-in form components use two-way data binding, where the property passed as @value or @checked is mutated by user interaction. This goes against the Data Down Actions Up principle, goes against Glimmer Components’ intention to have immutable arguments, and is discouraged by the Ember Core team.

Examples

This rule forbids the following:

<Input />
<Textarea></Textarea>

Migration

Many forms may be simplified by switching to a light one-way data approach.

For example – vanilla JavaScript has everything we need to handle form data, de-sync it from our source data and collect all user input in a single object.

import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';
import { action } from '@ember/object';

export default class MyComponent extends Component {
  @tracked userInput = {};

  @action
  handleInput(event) {
    const formData = new FormData(event.currentTarget);
    this.userInput = Object.fromEntries(formData.entries());
  }
}
<form {{on "input" this.handleInput}}>
  <label> Name
    <input name="name">
  </label>
</form>

Another option would is to "control" the field's value by replacing the built-in form component with a native HTML element and binding an event listener to handle user input.

In the following example the initial value of a field is controlled by a local tracked property, which is updated by an event listener.

import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';
import { action } from '@ember/object';

export default class MyComponent extends Component {
  @tracked name;

  @action
  updateName(event) {
    this.name = event.target.value;
  }
}
<input
  type="text"
  value={{this.name}}
  {{on "input" this.updateName}}
/>

You may consider composing the set helper with the pick helper to avoid creating an action within a component class.

<input
  type="text"
  value={{this.name}}
  {{on "input" (pick "target.value" (set this "name"))}}
/>

Related Rules

References