Almost every single app out there has some kind of form. It might be a user preference page or a core part of the experience you are providing your user. Usually, forms are bound to an object that the user can update or add more values. To provide rich user experience, validating the user's input might be a requirement, or to avoid confusing users, you might want to wait for the user to click "save" until updating parts of the screen that shows the data that the user is changing.
ChangesetForm helps to implement all these features in your apps. We use ember-changeset under the hood, validations are provided by ember-changeset-validations.
The <ChangesetForm>
component yields an object with all the components pre-bonded
to a changeset for you to create a form. It also exposes the changeset itself if
you need to access any information directly.
This shows how to have a complete form using ChangesetForm
.
{{#let (changeset this.model this.validations) as |changeset|}}
<ChangesetForm @changeset={{changeset}} as |Form|>
<Form.Input
@label='First Name'
@fieldName='firstName'
placeholder='What is your first name?'
/>
<Form.Input
@label='Last Name'
@fieldName='lastName'
@containerClass='mt-4'
placeholder='What is your family name?'
/>
<Form.Input
@label='Email Address'
@fieldName='email'
type='email'
@containerClass='mt-4'
/>
<Form.Textarea
@label='Comments'
@fieldName='comments'
@containerClass='mt-4'
/>
<Form.Select
@label='Countries'
@fieldName='countries'
@isMultiple={{true}}
@options={{this.countries}}
@allowClear={{true}}
@containerClass='mt-4'
as |option|
>
{{option}}
</Form.Select>
<Form.CheckboxGroup
@groupName='coreValues'
@label='Core Values'
@containerClass='mt-4'
as |Checkbox|
>
<Checkbox @label='Hungry' @fieldName='hungry' />
<Checkbox @label='Humble' @fieldName='humble' />
<Checkbox @label='Curious' @fieldName='curious' />
</Form.CheckboxGroup>
<Form.RadioGroup
@label='Favorite Meal'
@containerClass='mt-4'
@fieldName='favoriteMeal'
as |Radio|
>
<Radio @label='Breakfast' @value='breakfast' />
<Radio @label='Lunch' @value='lunch' />
<Radio @label='Dinner' @value='dinner' />
</Form.RadioGroup>
<Button
@type='submit'
@intent='primary'
disabled={{changeset.isInvalid}}
class='mt-4'
>
Save
</Button>
<Button @type='reset' @appearance='outlined'>
Reset
</Button>
<div class='mt-4'>
<code>
<p>isPristine: {{changeset.isPristine}}</p>
<p>isValid: {{changeset.isValid}}</p>
<p>isInvalid: {{changeset.isInvalid}}</p>
</code>
</div>
</ChangesetForm>
{{/let}}
The data model used to back the <ChangesetForm>
is an instance of a changeset object by ember-changeset
,
and so, nested property paths are supported. Ember Data Models are also supported.
import Component from '@glimmer/component';
export default class Demo extends Component {
myFormModel = {
name: {
first: 'Ron',
last: 'Swanson'
},
email: 'ron@pawnee.in.gov',
hometown: 'Pawnee, IN',
preferences: ['Lagavulin', 'Steak', 'Bacon', 'No govt.']
};
}
Changeset validation is provided by ember-changeset-validations,
and is optional for any Changeset
object used by <ChangesetForm>
components. This takes the form of another
JS object literal mapping properties to validation functions:
import Component from '@glimmer/component';
import validateFormat from 'ember-changeset-validations/validators/format';
import validatePresence from 'ember-changeset-validations/validators/presence';
export default class Demo extends Component {
// ...
myValidations = {
'name.first': validatePresence(true),
'name.last': validatePresence(true),
email: validateFormat({ type: 'email' }),
hometown: validatePresence(true),
preferences: validatePresence(true)
};
}
<ChangesetForm
@changeset={{changeset this.myFormModel this.myValidations}}
as |Form|
>
<!-- ... -->
</ChangesetForm>
In this case you are responsible for also providing any validation you wish to apply
to your Changeset
. The following is a brief example of that method:
import Component from '@glimmer/component';
import { Changeset } from 'ember-changeset';
import lookupValidator from 'ember-changeset-validations';
import validatePresence from 'ember-changeset-validations/validators/presence';
export default class Demo extends Component {
validations = {
myValue: validatePresence(true)
};
myModel = {
myValue: ''
};
myChangeset = new Changeset(
this.myModel,
lookupValidator(this.validations),
this.validations
);
}
<ChangesetForm @changeset={{this.myChangeset}} as |Form|>
<!-- ... -->
</ChangesetForm>
Element: <span class="hljs-built_in">Array</span>
Name | Type | Default | Description |
---|---|---|---|
changeset
*
|
BufferedChangeset
|
- | Changeset Object |
alwaysShowErrors
|
boolean
|
false
|
Always show errors if there are any |
onReset
|
function
|
- | Callback executed when from `onreset` event is triggered |
onSubmit
|
function
|
- | Callback executed when from `onsubmit` event is triggered |
runExecuteInsteadOfSave
|
boolean
|
false
|
Run Changeset execute method instead of save |
validateOnInit
|
boolean
|
false
|
Validate the changeset on initialization |
Name | Type | Default | Description |
---|---|---|---|
default
*
|
Array
|
- |
Name | Type |
---|---|
default |
[formComponents: FormComponents, changeset: BufferedChangeset, state: { hasSubmitted: boolean }] |
Here's the list of yielded form components from FormComponents
:
Key | Component |
---|---|
Text | <FormText> |
Textarea | <FormTextArea> |
Select | <FormSelect> |
Checkbox | <FormCheckbox> |
CheckboxGroup | <FormCheckboxGroup> |
Radio | <FormRadio> |
RadioGroup | <FormRadioGroup> |