Skip to content
This repository has been archived by the owner on Jul 27, 2023. It is now read-only.
/ ember-fit-form Public archive

A flexible component for form state management with data model adapters

License

Notifications You must be signed in to change notification settings

truecoach/ember-fit-form

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ember-fit-form

Build Status NPM Ember Observer Score

A form component which wraps a native <form> element and provides form state management abstractions.

ember-fit-form provides flexible state management for forms. We aim to support many data and validation libraries for use within your application's forms. We're also built on ember-concurrency, so you can easily support promise-aware hooks to manage your applications form state.

Please note that ember-fit-form does not provide form control components. It is simply an html form element with abstractions for state management.

Compatibility

  • Ember.js v3.16 or above
  • Ember CLI v2.13 or above
  • Node.js v10 or above

Installation

ember install ember-fit-form

Usage

Example

{{!-- my-form.hbs --}}
<FitForm @models={{model}} @oncancel={{action rollback}} @onsubmit={{action save}} as |form|>
  <input {{on "input" (action (mut model.name) value="target.value")}} />

  {{!-- other form content --}}

  <button {{on "click" form.cancel}} disabled={{form.isCancelling}}>
    {{if form.isCancelling "Cancelling..." "Cancel"}}
  </button>

  <button {{on "click" form.submit}} disabled={{form.isUnsubmittable}}>
    {{if form.isSubmitting "Saving..." "Save"}}
  </button>
</FitForm>
// my-form.js
rollback() {
  return model.rollbackAttributes();
},
save() {
  return model.save();
}

Form Adapters

This addon supports various data and validation libraries for use within your application's forms. You can even write your own custom adapters.

Built-in Adapters

  1. ember-changeset (default)

    ember-changeset model adapter

    Supported Validation Libraries:

  2. ember-model

    ember-data model adapter

    Supported Validation Libraries:

Each form adapter defines its own form action hooks, such as onsubmit and oncancel. The default behavior of these actions can be overwritten by passing onsubmit and oncancel into the component invocation. Please see source code for specifics on an adapter's default action hooks.

ex: onsubmit not passed to component

When onsubmit is not explicitly passed into the component, the adapter's default onsubmit action will be called on form submission.

<FitForm @models={{models}}>
  <!-- form.submit calls adapter's default `onsubmit` action -->
  <button {{on "click" form.submit}}>Save</button> 
</FitForm>

ex: onsubmit passed to component

When onsubmit is explicitly passed into the component, it will call this action in favor of the adapter's default onsubmit action on form submission.

<FitForm @models={{models}} @onsubmit={{action "submitMe"}}>
  <!-- form.submit calls "submitMe" action -->
  <button {{action form.submit}}>Save</button>
</FitForm>

Configuration

By default, ember-fit-form uses the ember-changeset adapter which expects Changeset models. To setup your default Model, you should configure the addon through config/environment:

module.exports = function(environment) {
  var ENV = {
    emberFitForm: {
      adapter: 'ember-changeset' // default
    }
  }
}

In the case that your forms use various Models throughout the application, you can overwrite the adapter at the component level.

<FitForm @models={{model}} @adapter="ember-model">
  {{!-- form content --}}
</FitForm>

API

Fit-Form Actions


submit

Submits the form.

Submitting a form calls the form's validate method and then calls the form's onsubmit hook if validation succeeds.

form.submit();
<button {{action form.submit}}>Submit</button>
{{!-- or --}}
<button onclick={{action form.submit}}>Submit</button>

The onsubmit hook will never be called if onvalidate hooks is rejected.

⬆️ back to top

cancel

Cancels the form. Cancelling a form calls the form's oncancel hook.

form.cancel();
<button {{action form.cancel}}>Cancel</button>
{{!-- or --}}
<button onclick={{action form.cancel}}>Cancel</button>

⬆️ back to top

validate

Validates the form. Validating a form calls the validate action for each of the form's models.

form.validate();
<button {{action form.validate}}>Check Validity</button>
{{!-- or --}}
<button onclick={{action form.validate}}>Check Validity</button>

⬆️ back to top

Fit-Form Component Action Hooks


Fit-Form adapters each contain action hooks. Some hooks call default functions, to reduce overall boilerplate code. For example, the ember-changeset adapter's onsubmit hook calls changeset.save() on each changeset by default. Declaring an onsubmit action on the component will override this behavior.

See default component action hook behavior:

The form object is always curried in as the last argument for all component action hooks.

onsubmit

The onsubmit hook action is a promise-aware action which is called on form submission. Form submission is triggered when calling form.submit().

<FitForm @models={{this.model}} @onsubmit={{this.save}} as |form|}}>
  <button {{form.submit}}>Save</button>
</FitForm>
save(/* form */) {
  return this.model.save();
}

The onsubmit hook will not be called on form submission if onvalidate hooks is rejected.

⬆️ back to top

onsuccess

The onsuccess hook is a promise-aware action which is called when the onsubmit hook is fulfilled.

<FitForm @models={{this.model}} @onsuccess={{this.success}} as |form|}}>
  <button {{form.submit}}>Save</button>
</FitForm>
success(/* result, form */) {
  // Do something
}

⬆️ back to top

onerror

The onerror hook is a promise-aware action which is called when the onsubmit hook is rejected.

<FitForm @models={{this.model}} @onerror={{this.error}} as |form|}}>
  <button {{form.submit}}>Save</button>
</FitForm>
error(/* error, form */) {
  // Do something
}

⬆️ back to top

oncancel

The oncancel hook is a promise-aware action which is called on form cancellation. Form cancellation is triggered when calling form.cancel().

<FitForm @models={{this.model}} @oncancel={{this.rollback}} as |form|}}>
  <button {{form.cancel}}>Cancel</button>
</FitForm>
rollback(/* form */) {
  return this.model.rollback();
}

⬆️ back to top

onvalidate

The onvalidate hook is a promise-aware action which is called on form validation. Form validation is triggered when calling form.validate() or form.submit() On form submission, if onvalidate returns a rejected Promise or false, the submission will reject, and onsubmit will not be called.

<FitForm @models={{this.model}} @onvalidate={{this.validate}} as |form|}}>
  <button {{form.validate}}>Check Fields</button>
  <button {{form.submit}}>Save</button>
</FitForm>
validate(/* form */) {
  return this.model.validate();
}

⬆️ back to top

oninvalid

The oninvalid hook is a promise-aware action which is called when the onvalidate hook is rejected or returns false.

<FitForm @models={{this.model}} @oninvalid={{this.invalid}} as |form|}}>
  <button {{form.submit}}>Save</button>
</FitForm>
invalid(/* error, form */) {
  // Do something
}

⬆️ back to top

Fit-Form Component Event Handler Hooks


The form object is always curried in as the last argument for all component event handler hooks.

onkeydown

When onkeydown is passed into fit-form component, it registers the keyDown event on the html form element. The onkeydown hook is called when the keyDown event is triggered.

<FitForm @models={{this.model}} @onkeydown={{this.handlekey}} as |form|}}>
  {{!-- form content --}}
</FitForm>
handlekey(event, form) {
  if (event.key === "Enter" && event.shiftKey) {
    // Shift + Enter
    form.submit();
  } else if (event.key === "Escape") {
    form.cancel();
  }
}

return true; to bubble the event. This is useful if you still want the form to handle the submit event.

⬆️ back to top

onkeyup

When onkeyup is passed into fit-form component, it registers the keyUp event on the html form element. The onkeyup hook is called when the keyUp event is triggered.

See onkeydown example for usage.

⬆️ back to top

onkeypress

When onkeypress is passed into fit-form component, it registers the keyPress event on the html form element. The onkeypress hook is called when the keyPress event is triggered.

See onkeydown example for usage.

⬆️ back to top

Fit-Form Attributes


isUnsubmittable

Returns a Boolean value of the form's (un)submittability.

form.isUnsubmittable; // true
<button {{action form.submit}} disabled={{form.isUnsubmittable}}>Submit</button>

You can still call submit if isUnsubmittable is true.

⬆️ back to top

isSubmittable

Returns a Boolean value of the form's submittability.

form.isSubmittable; // true
{{#if form.isSubmittable}}
  <span class=fa fa-check'></span>
{{/if}}

⬆️ back to top

isValid

Returns a Boolean value of the form's validity. A valid form is one where all of the form's models are valid.

form.isValid; // true
{{#if form.isValid}}
  <span class=fa fa-check'></span>
{{/if}}

⬆️ back to top

isInvalid

Returns a Boolean value of the form's (in)validity. A invalid form is one where the some of the form's models are invalid.

form.isInvalid; // true
{{#if form.isInvalid}}
  <span class=fa fa-times></span>
{{/if}}

⬆️ back to top

isDirty

Returns a Boolean value of the form's state. A dirty form is one with changes.

form.isDirty; // true

⬆️ back to top

isPristine

Returns a Boolean value of the form's state. A pristine form is one with no changes.

form.isPristine; // true

⬆️ back to top

isCancelling

Returns a Boolean value of the form's cancelling state. A cancelling form is one where the oncancel hook is pending. This attribute is commonly coupled with the cancel action.

form.isCancelling; // true
<button {{action form.cancel}} disabled={{form.isCancelling}}>Cancel</button>

⬆️ back to top

isSubmitting

Returns a Boolean value of the form's submitting state. A submitting form is one where the onsubmit, onsuccess, or onerror hooks are pending. This attribute is commonly coupled with the submit action.

form.isSubmitting; // true
<button {{action form.submit}} disabled={{form.isSubmitting}}>Submit</button>

⬆️ back to top

isValidating

Returns a Boolean value of the form's validating state. A validating form is one where the form's model(s) are validating upon form submission.

form.isValidating; // true
{{#if form.isValidating}}
  <span class=fa fa-spinner></span>
{{/if}}

⬆️ back to top

didCancel

Returns a Boolean value of the form's cancelled state. A cancelled form is one where the oncancel hooks is settled.

form.didCancel; // true
{{#if form.didCancel}}
  <span class='error'>{{model.errors}}</span>
{{/if}}

⬆️ back to top

didSubmit

Returns a Boolean value of the form's submitted state. A submitted form is one where the onsubmit hooks is settled.

form.didSubmit; // true
{{#if form.didSubmit}}
  <span class='error'>{{model.errors}}</span>
{{/if}}

⬆️ back to top

didValidate

Returns a Boolean value of the form's validated state. A validated form is one where the form's model(s) were validated upon form submission.

form.didValidate; // true
{{#if form.didValidate}}
  <span class='error'>{{model.errors}}</span>
{{/if}}

⬆️ back to top

Custom Adapters


Generate a form adapter

$ ember generate form-adapter foo-bar

This creates app/form-adapters/foo-bar.js and a unit test at tests/unit/form-adapters/foo-bar-test.js. By default, the form-adapter extends the BaseAdapter.

Example - extend ember-changeset form-adapter

In this example, we'll extend the ember-changeset form-adapter. We will overwrite the default oncancel action to never call rollbackAttributes() on the Changesets.

  • Generate form-adapter

    ember g form-adapter ember-changeset/no-rollbacks

  • Extend the ember-changeset form-adapter

    // app/form-adapters/ember-changeset/no-rollbacks;
    import EmberChangesetAdapter from 'ember-fit-form/form-adapters/ember-changeset';
    export default class EmberChangesetNoRollbacksAdapter extends EmberChangesetAdapter {
      oncancel() { /* noop - ie. no rollbackAttributes */ }
    }
  • Define adapter on component or configuration

    {{!-- my-template.hbs --}}
    <FitForm @models={{changeset}} @adapter="ember-changeset/no-rollbacks">
      {{!-- other form content --}}
    </FitForm>

License

This project is licensed under the MIT License.