Form Component API
The matestack_form
core component is a Vue.js driven component. It enables you to implement dynamic forms without writing JavaScript. It relies on child components to collect and submit user input: form_input
, form_textarea
, form_radio
, form_select
, and form_checkbox
. They are described on their own documentation page
Parameters
The core form component accepts the following parameters. Pass them in as a hash like so:
For - required
The form
component wraps the input in an object. The name of this object can be set in multiple ways:
set as a symbol
When submitting this form, the form
component will perform a request with a payload like this:
set by Active Record class name
When submitting this form, the form
component will perform a request with a payload like this:
Please be aware that if you use an Active Record model, the keys of the input components should match a model attribute/method. The form automatically tries to prefill the form inputs through calling the keys as methods on the model.
Method - required
This specifies which kind of HTTP method should get triggered. It accepts a symbol like so:
Path - required
This parameter accepts a typical Rails path
or
Emit
This event gets emitted right after form submit. In contrast to the success
or failure
events, it will be emitted regardless of the server response.
Delay
You can use this attribute if you want to delay the actual form submit request. It will not delay the event specified with the emit
attribute.
Multipart
If you want to perform file uploads within this form, you have to set multipart
to true. It will send the form data with "Content-Type": "multipart/form-data"
Success
The success part of the matestack_form
component gets triggered once the controller action we wanted to call returns a success code, usually the 2xx
HTTP status code.
Emit event
To trigger further behavior, we can configure the success part of a form
to emit a message like so:
Perform transition
We can also perform a transition that only gets triggered on success and also accepts further params:
When the server redirects to a url, for example after creating a new record, the transition needs to be configured to follow this redirect of the server response.
A controller action that would create a record and then respond with the url the page should transition to, could look like this:
Same applies for the failure
configuration.
Perform redirect
We can also perform a redirect (full page load) that only gets triggered on success and also accepts further params:
Please be aware, that emiting a event doen't have an effect when performing a redirect instead of a transition, as the whole page (including the surrounding app) gets reloaded!
When the server redirects to a url, for example after creating a new record, the redirect needs to be configured to follow this redirect of the server response.
A controller action that would create a record and then respond with the url the page should redirect to, could look like this:
Same applies for the failure
configuration.
Reset form
If submitted successfully, the form
component resets its state by default when using the "post" method. When using the "put" method, the state is not resetted by default. You may control this behavior explictly by using the reset
option:
Failure
As counterpart to the success part of the form
component, there is also the possibility to define the failure behavior. This is what gets triggered after the response to our form
submit returns a failure code, usually in the range of 400
or 500
HTTP status codes.
Emit event
To trigger further behavior, we can configure the failure part of an action to emit a message like so:
Perform transition
We can also perform a transition that only gets triggered on failure:
Reset form
The form
component does not reset its state by default when not submitted successfully. You may control this behavior explictly by using the reset
option:
ID
This parameter accepts a string of ids that the form component should have:
which renders as an HTML id
attribute, like so:
Class
This parameter accepts a string of classes that the form component should have:
which renders as an HTML class
attribute, like so:
Error rendering
If the server is responding with a well formatted error response and status after submitting the form, matestack will automatically render server error messages right next to the corresponding input (matching error and input key). Additionally the input itself will get a 'error' css class; the parent form will get a 'has-errors' css class.
The described approach is suitable for all form_* input components.
By default it would look like this:
when a 4xx
JSON server response like that was given (ActiveRecord errors format):
You can modify the error rendering like this:
On input level
On form level
Configuring errors on a per form basis. Per form field configs take precedence over the form config.
Outputs errors as:
Error message rendering
Given a server error response like that:
now including a message
which is not mapped to an input field, we can display this error message like:
The matestack_form
component emits the event together with all errors and the message coming from the server's response. The toggle
component can then access all this data via event.data.xyz
Loading state
The form will get a 'loading' css class while submitting the form and waiting for a server response:
If you simply want to disable your submit button, you can use a simple Vue.js binding:
If you want to adjust the submit element more flexible while the form is being submitted, you could use the event mechanism of the form in combination with the toggle
component:
in combination with a form config like this:
Form and other Vue.js components
The child components form_*
have to be placed within the scope of the parent form
component, without any other Vue.js component like toggle
, async
creating a new scope between the child component and the parent form component**
We're working on a better decoupling of the form child components. In the mean time you can enable dynamic child component rendering utilizing Vue.js directly. We will shortly publish a guide towards that topic.
Examples
These examples show generic use cases and can be used as a guideline of what is possible with the form core component.
Beforehand, we define some example routes for the form input in our config/routes.rb
:
We also configure our example controllers to accept form input and react in a predictable and verbose way:
Async submit request with clientside payload
On our example page, we define a form that accepts text input and has a submit button.
When we visit localhost:3000/example
, fill in the input field with bar and click the submit button, our FormTestController
receives the input.
Furthermore, our bar input disappears from the input field - Easy!
Async submit request with failure event
This time, we break the form input on purpose to test our failure message! Again, we define our example page. Notice that we explicitly aim for our failure_form_test_path
.
Now, when we visit our example page on localhost:3000/example
and fill in the input field with e.g. bar and hit the submit button, we get displayed both server says: form had errors
and 'foo': [ 'seems to be invalid' ]
. Just what we expected to receive!
Async submit request with success transition
In this example, things get a bit more complex. We now want to transition to another page of our application after successfully submitting a form!
In order to additionally show a success/failure message, we define our matestack app layout with messages, using the async core component:
On our first example page, we define our form to transfer us to the second page (form_test_page_2_path
) on successful input:
On the second example page, we aim for our failure path (failure_form_test_path
) on purpose and define our form to transfer us to the first page (form_test_page_1_path
) on failed input:
Of course, to reach our two newly defined pages, we need to make them accessible through a controller:
We also need to extend our routes in config/routes.rb
to handle the new routes:
Now, if we visit localhost:form_test/page1
, we can fill in the input field with e.g. bar and click the submit button.
We then get displayed our nice success message (server says: form submitted successfully
) and get transferred to our second page.
If we fill in the the input field there and hit the submit button, we not only see the failure messages (server says: form had errors
and 'foo': [ 'seems to be invalid' ]
), we also get transferred back to the first page, just the way we specified this behavior in the page definition above!
Async submit request with success transition - dynamically determined by server
In the example shown above, the success
transition
is statically defined. Sometimes the transition
needs to be dynamically controlled within the server action. Imagine creating a new Active Record instance with a form
. If you want to show the fresh instance on another page and therefore want to define a transition
after successful form submission, you would need to know the ID of the fresh instance! That is not possible, as the ID is auto-generated and depends on the current environment/state. Therefore you can tell the form
component to follow a transition, which the server action defines after creating the new instance (and now knowing the ID):
On the page
:
On the controller
action
:
Multiple input fields of different types
Of course, our input core component accepts not only 'text', but very different input types: In this example, we will introduce 'password', 'number', 'email', 'range' types!
On our example page, we define the input fields, together with a type: X
configuration:
Now, we can visit localhost:3000/example
and fill in the input fields with various data that then gets sent to the corresponding path, in our case success_form_test_path
.
Initialization with a value
Our form_input field doesn't need to be empty when we load the page. We can init
it with all kinds of values:
Now, when we visit localhost:3000/example
, we see our input field already welcomes us with the value some value!
Pre-filling the input field with a placeholder
Instead of a predefined value, we can also just show a placeholder in our form_input component:
Now, when we visit localhost:3000/example
, the input field is technically empty, but we see the text some placeholder. In contrary to the init
value in example 5, the placeholder can't get submitted)!
Defining a label
Another useful feature is that we can also give a label to our form_input!
Now, when we visit localhost:3000/example
, the input field carries a some label-label.
Asynchronously display error messages
Here, we aim for the failure_form_test_path
on purpose to check how error messages are handled!
If we head to localhost:3000/example
, and fill in the input field with, e.g., text and click the submit button, we will get displayed our error message of seems to be invalid
. Neat!
Mapping the form to an Active Record Model
To test the mapping of our form to an Active Record Model, we make sure our TestModel
's description can't be empty:
Now, on our example page, we prepare a new instance of our TestModel
that we then want to save through the form component:
Notice that we only prepared the title, but missed out on the description.
If we head to our example page on localhost:3000/example
, we can already see the title input field filled in with Title. Trying to submit the form right away gives us the error message (can't be blank
) because the description is, of course, still missing!
After filling in the description with some input and hitting the submit button again, the instance of our TestModel
gets successfully saved in the database - just the way we want it to work.
Last updated