Ember Form Builder is an Ember Addon that enables you to assemble forms with labels, validations, hints without repeating yourself. It's strongly inspired by a Rails gem SimpleForm.
- Ember.js v3.28 or above
- Ember CLI v3.28 or above
- Node.js v14 or above
ember install ember-form-builder
The following inputs types are built into Ember Form Builder:
Type | Guessed when | HTML form |
---|---|---|
boolean |
attribute name begins with is , has or did ; underlying model's attribute is a DS.attr("boolean") |
<input type="checkbox" /> |
collection |
a collection attribute is present |
<select> |
date |
underlying model's attribute is a DS.attr("date") |
<input type="date" /> |
email |
attribute name contains email |
<input type="email" /> |
number |
underlying model's attribute is a DS.attr("number") |
<input type="number" /> |
password |
attribute name contains password |
<input type="password" /> |
checkboxes |
never | collection of <input type="radio" /> with labels |
string |
underlying model's attribute is a DS.attr("string") |
<input type="text" /> |
text |
never | <textarea> |
You can easily extend any of the above inputs by overriding them and requiring addon's original component. Like this:
// In app/components/inputs/text-input.js
import OriginalTextInput from 'ember-form-builder/components/inputs/text-input';
import { action, set } from '@ember/object';
export default class TextInput extends OriginalTextInput {
@action
handleChange(e) {
set(this, 'args.config.value', e.target.value.trim());
}
}
To provide your own input types simply implement a component named your-type-input
and put it into components/inputs
folder. Like this:
You can then use your input using the as
option:
Ember Form Builder will automatically mark inputs as erroneus and display error messages next to them whenever:
- the input's attribute is invalid and
- a user has focused out of the input at least once.
Ember Form Builder supports these validation addons:
- Ember Validations (default) - this is compatible also with Ember Data's server-provided validation messages,
- Ember CP Validations
You can set your choice in the configuration. None of those libraries is required by Ember Form Builder.
Ember Form Builder will automatically detect model name and then use it in input name generation and translation lookup.
Ember Form Builder supports these data addons:
- Ember Data (default),
- Ember-Orbit
You can set your choice in the configuration. None of those libraries is required by Ember Form Builder.
You can easily cusomize labels and other texts by providing values through attributes in your templates but if your app is or might become international you might wish to integrate the forms with an internationalization library.
Ember Form Builder supports ember-intl
and ember-i18n
at the moment, however other solutions might be integrated in the future.
Ember Form Builder automatically detects internationalization addon and tries to guess the translation keys.
use case | label | hint | placeholder | submit | required |
---|---|---|---|---|---|
Explicit | @label="My attribute" |
@hint="My hint" |
@placeholder="My placeholder" |
@text="My submit" |
not possible |
Custom form translation key: <FormBuilder @translationKey="custom.key" /> |
Looks up custom.key.attributes.attribute |
Looks up custom.key.hints.attribute |
Looks up custom.key.placeholders.attribute |
Looks up custom.key.actions.submit |
not possible |
Underlying model's name (e.g. article ) |
Looks up article.attributes.attribute |
Looks up article.hints.attribute |
Looks up article.actions.submit |
Looks up article.placeholders.attribute |
not possible |
Default | humanizes attribute name | empty | empty | Looks up formBuilder.actions.submit |
Looks up formBuilder.isRequired |
Without ember-intl or ember-i18n |
humanizes attribute name | empty | empty | "Save" | "Required" |
Ember Form Builder uses input wrappers to allow you control the generated HTML and CSS.
Input wrapper is a component that receives @inputComponent
, @labelComponent
and @config
arguments.
@inputComponent
and @labelComponent
are preconfigured component instances, so you can
easily render them in any place you need.
@config
is a hash containing some predefined keys (inputElementId
, name
, value
, texts
,
validations
, canValidate
), as well as all arguments that you pass to
the <f.input />
call.
Ember Form Builder ships with default
and inline
wrappers that are compatible
with Bootstrap form markup. You can overwrite them or
define your own wrappers. Then to select a wrapper for a specific input, just pass
its name in the @wrapper
attribute:
Ember Form Builder can be configured via environment configuration. To override them, specify your values in config/environment.js
under formBuilder
key (e.g. ENV.formBuilder.validationsAddon = 'ember-cp-validations'
).
Those are the default values:
{
validationsAddon: 'ember-validations', // name of the validations addon. Supported values: "ember-validations" and "ember-cp-validations"
dataAddon: 'ember-data' // name of the data addon. Supported values: "ember-data" and "ember-orbit"
}
Ember Form Builder ships with several test helpers that allow you to easily read and write form data.
In the examples below we use this component:
import { module, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';
import { render } from '@ember/test-helpers';
import { readForm } from 'ember-form-builder/test-support';
import hbs from 'htmlbars-inline-precompile';
module('Integration | Components | my-form', function(hooks) {
setupRenderingTest(hooks);
test('it displays model data', async function(assert) {
this.model = {
firstName: 'Jan',
age: 37,
children: [
{ firstName: 'Anna' }
]
};
await render(hbs`<MyForm @model={{this.model}} />`);
// pass a list of attributes to read...
assert.deepEqual(readForm('person', ['firstName', 'age', 'children.0.firstName']), this.model);
// ...or just an object (its keys will be converted to paths)
assert.deepEqual(readForm('person', this.model), this.model);
});
import { module, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';
import { render } from '@ember/test-helpers';
import { fillForm, pick } from 'ember-form-builder/test-support';
import hbs from 'htmlbars-inline-precompile';
module('Integration | Components | my-form', function(hooks) {
setupRenderingTest(hooks);
test('it updates data', async function(assert) {
this.model = {
firstName: 'Jan',
age: 37,
children: [
{ firstName: 'Anna' }
]
};
await render(hbs`<MyForm @model={{this.model}} />`);
let newData = {
firstName: 'Viktor',
age: 32,
children: [
{ firstName: 'Joanna' }
]
};
await fillForm('person', newData);
assert.deepEqual(this.model, newData);
// if your model includes attributes that should not be compared, use the pick() helper:
this.model.lastName = 'Larsson';
assert.deepEqual(pick(this.model, newData), newData);
});
import { module, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';
import { render } from '@ember/test-helpers';
import { fillForm, readErrors } from 'ember-form-builder/test-support';
import hbs from 'htmlbars-inline-precompile';
module('Integration | Components | my-form', function(hooks) {
setupRenderingTest(hooks);
test('it validates data', async function(assert) {
this.model = {
age: 37
};
await render(hbs`<MyForm @model={{this.model}} />`);
await fillForm('person', {
age: 2
});
let errors = {
age: 'must be greater than or equal to 16'
}
assert.deepEqual(readErrors('person', errors), errors);
});
Please check out the upgrading documentation.
nibynic © 2015