Skip to content

Commit 91360ae

Browse files
committed
improved form guide
1 parent cead057 commit 91360ae

File tree

1 file changed

+156
-30
lines changed

1 file changed

+156
-30
lines changed
Lines changed: 156 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,74 +1,200 @@
11
# Forms
22

3-
Forms are one of the most important components for a lot of applications as they are always needed for user input, like searches, logins, registrations, newsletter subscriptions and much more. Matestack implements a `form` component which could wrap differnt form input components also implemented in matestack like text inputs, number inputs, textareas, selects, checkboxes, radio buttons and more.
4-
3+
Matestack implements a `form` component which wraps different child form input components like text inputs, number inputs, textareas, selects, checkboxes, radio buttons and more in order to collect user input. The `form` component is implemented with Vue.js and is designed to submit its data via an async HTTP request, usually targeting a Rails controller action through Rails routing, when submitted. The `form` can reset itself, render serverside errors, emit events and perform transitions without full browser page reload after form submission. Alternatively, the form can perform a redirect resulting in a full browser page reload after submission.
54

65
## Usage
76

8-
### Form component
7+
### Basic configuration
98

10-
Like in Rails with `form_for` you can create a form in matestack with `form`. It takes a hash as parameter with which you can configure your form and a block with the formular content. In the config hash you can set the HTTP request method, a path, success and failure behavior. You also need to specify a model, string or symbol for what the form is for. All form params will then be submitted nested in this namespace, following Rails behavior and conventions.
9+
Like Rails' `form_for`, Matestack's `form` takes a hash as parameter which is used to configure the form. Within the block of a `form`, child components like `form_input` are used to collect user input:
1110

1211
```ruby
1312
def response
1413
form form_config do
15-
form_input key: :name, label: 'Name'
16-
# form content goes here
14+
form_input key: :name, type: :text, label: 'Name'
15+
# ...
16+
form_submit do
17+
button text: "submit", type: :submit
18+
end
1719
end
1820
end
1921

22+
# we use a helper method to define the config hash
2023
def form_config
2124
{
2225
for: User.new
2326
path: users_path,
24-
method: :post,
25-
success: {
26-
transition: {
27-
follow_redirect: true
28-
}
29-
},
30-
failure: {
31-
emit: 'user_form_failure'
32-
}
27+
method: :post
3328
}
3429
end
3530
```
3631

37-
Each form requires a few keys for configuration: `:for`, `:path`, `:method`.
32+
Each form requires a few keys for configuration: `:for`, `:path`, `:method`.
33+
34+
* `:for` can reference an active record object or a string/symbol which will be used to nest params in it. The name of that surrounds the params is either the given string/symbol or derived from the active record object. In the above case the submitted params will look as follows: `user: { name: 'A name }`.
35+
36+
* `:path` specifies the target path, the form is submitted to (can be a plain String or a Rails url helper method)
37+
38+
* `:method` sets the HTTP method the form is using to submit its data, can be `:post` or `:put`
39+
40+
### Serverside action
41+
42+
As mentioned, the `form` posts its data to a specified Rails controller action through Rails routing.
43+
44+
**Be aware to change the response structure of scaffolded Rails controller action like seen below in order to support a Matestack form:**
45+
46+
`app/controllers/users_controller.rb`
47+
48+
```ruby
49+
class UsersController < ApplicationController
50+
51+
# ...
52+
53+
def create
54+
@user = User.new(user_params)
55+
56+
# instead of:
57+
58+
# respond_to do |format|
59+
# if @user.save
60+
# format.html { redirect_to @user, notice: 'User was successfully created.' }
61+
# format.json { render :show, status: :created, location: @user }
62+
# else
63+
# format.html { render :new }
64+
# format.json { render json: @user.errors, status: :unprocessable_entity }
65+
# end
66+
# end
67+
68+
# do this:
69+
70+
if @user.save
71+
render json: {
72+
message: 'User was successfully created.'
73+
}, status: :created
74+
else
75+
render json: {
76+
errors: @user.errors,
77+
message: 'User could not be created.'
78+
}, status: :unprocessable_entity
79+
end
80+
end
81+
82+
# ...
3883

39-
* `:for` can reference an active record object or a string/symbol which will be used to nest params in it. The name of that sorrounds the params is either the given string/symbol or derived from the active record object. In the above case the submitted params will look as follows: `user: { name: 'A name }`.
84+
private
4085

41-
* `:path` specifies the target path, the form is submitted to
42-
43-
* `:method` sets the request method the form is submitted with
86+
# Only allow a list of trusted parameters through.
87+
def user_params
88+
params.require(:user).permit(:name)
89+
end
90+
91+
end
92+
```
4493

4594
### Form success and failure behavior
4695

47-
Forms will be submitted asynchronously and in case of errors dynamically extended to show errors belonging to inputs fields. In case of a successful request the form is resetted.
96+
Forms will be submitted asynchronously and in case of errors dynamically extended to show errors belonging to inputs fields. In case of a successful request the form is resetted, unless configured not to do so.
4897

4998
**Rendering errors**
5099

51-
If the request failes and the server responds with json containing an `:errors` key and errors following the active record error schema the form automatically renders these errors below the input and adds a css class "error" to the input and "has-errors" to the form.
100+
If the request fails and the server responds with JSON containing an `:errors` key and errors following the ActiveRecord error schema, the form automatically renders these errors below the inputs and adds a css class `error` to the each input with an error and `has-errors` to the wrapping form tag:
101+
102+
```html
103+
<form class="matestack-form has-errors">
104+
<input type="text" class="error">
105+
<span class="errors">
106+
<!-- for each error string within the error array for the key 'title' -->
107+
<span class="error">
108+
can't be blank
109+
</span>
110+
</span>
111+
</form>
112+
```
113+
114+
In order to change the DOM structure and applied CSS classes for errors, you can adjust the error rendering via configuration. The code below for example is meant to support Bootstrap form error classes:
52115

53-
Read more about error rendering and customizing form errors at the [form api documentation](/docs/api/100-components/form.md).
116+
```ruby
117+
def form_config
118+
{
119+
for: User.new
120+
path: users_path,
121+
method: :post,
122+
errors: {
123+
wrapper: { tag: :div, class: 'invalid-feedback' },
124+
input: { class: 'is-invalid' }
125+
}
126+
end
127+
```
128+
129+
Read more about error rendering and customizing form errors at the [form api documentation](/docs/api/100-components/form.md).
54130

55131
**Customizing success and failure behavior**
56132

57-
We can customize the success and failure behavior of an `form` component by specifiyng the `:success` or `:failure` key with a hash as value. The value hash can contain different keys for different behavior.
133+
We can customize the success and failure behavior of a `form` component by specifiyng the `:success` or `:failure` key with a hash as value. The value hash can contain different keys for different behavior.
58134

59-
* use `:emit` inside it to emit an event for success or failed responses.
135+
* use `:emit` inside it to emit an event for success or failed responses.
60136
* use `:transition` to transition to another page. Either specifiyng a hash containing a path and optional params or a hash with `follow_response: true` in order to follow the redirect of the response.
61137
* use `:redirect` with a hash containing a path and params or `follow_response: true` to redirect the browser to the target. Be aware that this will trigger a full website reload as it is a redirect and no transition.
62138

63139
You can also combine `:emit` and one of `:transition`, `:redirect` if wanted.
64140

65-
Read more about success and failure behavior customization at the [form api documentation](/docs/api/100-components/form.md).
141+
If you want to show a separate success or error message after form submission, you can use the emitted events in order to trigger `toggle` components to show up for a 5 seconds:
142+
143+
```ruby
144+
def response
145+
form form_config do
146+
form_input key: :name, type: :text, label: 'Name'
147+
# ...
148+
form_submit do
149+
button text: "submit", type: :submit
150+
end
151+
end
152+
toggle show_on: "submitted", hide_after: 5000 do
153+
span class: "some-success-styling", text: "Yeah! It worked!"
154+
end
155+
toggle show_on: "failed", hide_after: 5000 do
156+
span class: "some-error-styling", text: "Damn! Somenthing went wrong!"
157+
end
158+
end
159+
160+
def form_config
161+
{
162+
for: User.new
163+
path: users_path,
164+
method: :post,
165+
success: {
166+
emit: "submitted"
167+
},
168+
failure: {
169+
emit: "failed"
170+
}
171+
end
172+
```
173+
174+
This will render static texts "Yeah! It worked!" or "Damn! Somenthing went wrong!" in a `span` after form submission. If you want to render the serverside message defined with `render json: {
175+
message: 'User was successfully created.'
176+
}, status: :created`, you would have to add some minor Vue.js to the `toggle` content:
177+
178+
```ruby
179+
# ...
180+
toggle show_on: "submitted", hide_after: 5000 do
181+
span class: "some-success-styling", text: "Yeah! {{ event.data.message }}"
182+
end
183+
toggle show_on: "failed", hide_after: 5000 do
184+
span class: "some-error-styling", text: "Damn! {{ event.data.message }}"
185+
end
186+
# ...
187+
```
188+
189+
Now the serveside messages will appear within the `toggle` components.
190+
191+
Read more about success and failure behavior customization at the [form api documentation](/docs/api/100-components/form.md).
66192

67193
### Input components
68194

69-
Inside a form you can use our form input components `form_input`, `form_textarea`, `form_select`, `form_radio` and `form_checkbox`. Do not use the basic input components `input`, `textarea` and so on, because they do not work with matestack forms. Instead use the _form input_ components. Each input component requires a `:key` which represents the params name as which this inputs value get's submitted. If you specified an active record object or similar in the `form` with the `:for` options, inputs will be prefilled with the value of the corresponding attribute or method of the object. It is also possible to specify `:label` in order to create labels for the input on the fly.
195+
Inside a form you can use our form input components `form_input`, `form_textarea`, `form_select`, `form_radio` and `form_checkbox`. Do not use the basic input components `input`, `textarea` and so on, because they do not work with matestack forms. Instead use the _form input_ components. Each input component requires a `:key` which represents the params name as which this inputs value get's submitted. If you specified an active record object or similar in the `form` with the `:for` options, inputs will be prefilled with the value of the corresponding attribute or method of the object. It is also possible to specify `:label` in order to create labels for the input on the fly.
70196

71-
* `form_input` - Represents a html "input". All w3c specified input types are supported by this component, just pass in your wanted type with the `:type` option.
197+
* `form_input` - Represents a html "input". All w3c specified input types are supported by this component, just pass in your wanted type with the `:type` option.
72198
```ruby
73199
form_input key: :name, type: :text, label: 'Name'
74200
form_input key: :age, type: :number, label: 'Age'
@@ -81,7 +207,7 @@ Inside a form you can use our form input components `form_input`, `form_textarea
81207
```
82208

83209
* `form_select` - Represents a html "select". You can pass in values as array or hash under the `:options` key, which then will be rendered as html "option"s. Given an array, value and label will be the same, in case of a hash the key will be used as label and the value as value. In order to allow multiple selections set `multiple: true`. Selected values will then be submitted in an array.
84-
210+
85211
* `form_checkbox` - Represents a html "input type=checkbox". You can pass in values as array or hash under the `:options` key, which then will be rendered as checkboxes. Given an array, value and label will be the same, in case of a hash the key will be used as label and the value as value. Selected values will then be submitted in an array. If you leave out `:options` a simple single checkbox will be rendered and a hidden input like rails does which allows for a "true" or "false" checkbox.
86212

87213
* `form_radio` - Represents a html "input type=radio". You can pass in values as array or hash under the `:options` key, which then will be rendered as radio buttons. Given an array, value and label will be the same, in case of a hash the key will be used as label and the value as value.
@@ -95,4 +221,4 @@ Wrap a button or any markup which should submit the form when clicked in `form_s
95221

96222
## Complete documentation
97223

98-
If you want to know all details about the `form` component and all inputs and their usage as well as how you can customize errors, input placeholder, input value initialization and more checkout it's [api documentation](/docs/api/100-components/form.md).
224+
If you want to know all details about the `form` component and all inputs and their usage as well as how you can customize errors, input placeholder, input value initialization and more checkout it's [api documentation](/docs/api/100-components/form.md).

0 commit comments

Comments
 (0)