Skip to content

Commit 69ccfea

Browse files
author
Nils Henning
committed
make form errors customizable
1 parent c29b63d commit 69ccfea

File tree

10 files changed

+266
-83
lines changed

10 files changed

+266
-83
lines changed

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -400,6 +400,14 @@ And use it in your view as follows:
400400
To render existing rails views inside your components or pages use the new `rails_view` component. It replaces the old `html` component.
401401
You can render existing partials and views with this helper anywhere in your app. For further information read the `rails_view` documentation.
402402

403+
### Changed Components
404+
405+
#### Form Input Component
406+
407+
The `form_input` component no longer supports the input type textarea.
408+
Textareas were extracted in a component and can be used with `textarea` or in forms with `form_textarea`.
409+
The `form_input`component now supports all types according to W3Cs possible types.
410+
403411
### Removed Components
404412

405413
#### Inline Component
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
module Matestack::Ui::Core::Form::HasErrors
2+
3+
def self.included(base)
4+
base.class_eval do
5+
optional :errors
6+
end
7+
end
8+
9+
def error_key
10+
"errors['#{key.to_s}']"
11+
end
12+
13+
# error partial
14+
# overwrite render_errors to customize error rendering
15+
def render_errors
16+
unless errors == false
17+
self.send(wrapper_tag, class: wrapper_class, attributes: { 'v-if': error_key }) do
18+
self.send(error_tag, class: error_class, attributes: { 'v-for': "error in #{error_key}" }) do
19+
plain '{{ error }}'
20+
end
21+
end
22+
end
23+
end
24+
25+
def wrapper_tag
26+
tag = errors.is_a?(Hash) ? errors.dig(:wrapper, :tag) : :span
27+
tag || :span
28+
end
29+
30+
def wrapper_class
31+
klaas = errors.is_a?(Hash) ? errors.dig(:wrapper, :class) : 'errors'
32+
klaas || 'errors'
33+
end
34+
35+
def error_tag
36+
tag = errors.is_a?(Hash) ? errors.dig(:tag) : :span
37+
tag || :span
38+
end
39+
40+
def error_class
41+
tag = errors.is_a?(Hash) ? errors.dig(:class) : 'error'
42+
tag || 'error'
43+
end
44+
45+
end

app/concepts/matestack/ui/core/form/input/input.haml

Lines changed: 0 additions & 46 deletions
This file was deleted.

app/concepts/matestack/ui/core/form/input/input.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
require_relative '../utils'
2+
require_relative '../has_errors'
23
module Matestack::Ui::Core::Form::Input
34
class Input < Matestack::Ui::Core::Component::Static
45
include Matestack::Ui::Core::Form::Utils
6+
include Matestack::Ui::Core::Form::HasErrors
57

68
html_attributes :accept, :alt, :autocomplete, :autofocus, :checked, :dirname, :disabled, :form, :formaction,
79
:formenctype, :formmethod, :formnovalidate, :formtarget, :height, :list, :max, :maxlength, :min, :minlength,
@@ -13,9 +15,7 @@ class Input < Matestack::Ui::Core::Component::Static
1315
def response
1416
label text: input_label if input_label
1517
input html_attributes.merge(attributes: vue_attributes)
16-
span class: 'errors', attributes: { 'v-if': error_key } do
17-
span class: 'error', text: '{{ error }}', attributes: { 'v-for': "error in #{error_key}" }
18-
end
18+
render_errors
1919
end
2020

2121
def vue_attributes

app/concepts/matestack/ui/core/form/select/select.haml

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,7 @@
1717
- if options[:options].is_a?(Array)
1818
- options[:options].each do |option|
1919
%option{value: option}=option
20-
%span{class: "errors", "v-if": error_key }
21-
%span{class: "error", "v-for": "error in #{error_key}"}
22-
{{ error }}
20+
= render_errors
2321

2422
- if options[:type] == :checkbox
2523

@@ -46,9 +44,7 @@
4644
name: value,
4745
value: value}/
4846
%label{for: id_for_option(value)}=value
49-
%span{class: "errors", "v-if": error_key }
50-
%span{class: "error", "v-for": "error in #{error_key}"}
51-
{{ error }}
47+
= render_errors
5248

5349
- if options[:type] == :radio
5450

@@ -75,6 +71,4 @@
7571
name: "#{attr_key}_#{value}",
7672
value: value}/
7773
%label{for: id_for_option(value)}=value
78-
%span{class: "errors", "v-if": error_key }
79-
%span{class: "error", "v-for": "error in #{error_key}"}
80-
{{ error }}
74+
= render_errors

app/concepts/matestack/ui/core/form/select/select.rb

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
1+
require_relative '../utils'
2+
require_relative '../has_errors'
13
module Matestack::Ui::Core::Form::Select
24
class Select < Matestack::Ui::Core::Component::Static
5+
include Matestack::Ui::Core::Form::Utils
6+
include Matestack::Ui::Core::Form::HasErrors
37

8+
requires :key
49
requires options: { as: :select_options }
510

611
def setup
@@ -9,21 +14,13 @@ def setup
914
end
1015
end
1116

12-
def input_key
13-
'data["' + options[:key].to_s + '"]'
14-
end
15-
16-
def error_key
17-
'errors["' + options[:key].to_s + '"]'
18-
end
19-
2017
def attr_key
21-
options[:key].to_s
18+
key.to_s
2219
end
2320

2421
def option_values
25-
values = options[:options] if options[:options].is_a?(Array)
26-
values = options[:options].keys if options[:options].is_a?(Hash)
22+
values = select_options if select_options.is_a?(Array)
23+
values = select_options.keys if select_options.is_a?(Hash)
2724
return values
2825
end
2926

@@ -47,16 +44,16 @@ def init_value
4744
end
4845

4946
unless options[:for].nil?
50-
value = options[:for].send(options[:key])
47+
value = options[:for].send(key)
5148
if [true, false].include? value
5249
value ? 1 : 0
5350
else
5451
return value
5552
end
5653
else
5754
unless @included_config.nil? && @included_config[:for].nil?
58-
if @included_config[:for].respond_to?(options[:key])
59-
value = @included_config[:for].send(options[:key])
55+
if @included_config[:for].respond_to?(key)
56+
value = @included_config[:for].send(key)
6057
if [true, false].include? value
6158
value ? 1 : 0
6259
else
@@ -67,7 +64,7 @@ def init_value
6764
return nil
6865
end
6966
if @included_config[:for].is_a?(Hash)
70-
return @included_config[:for][options[:key]]
67+
return @included_config[:for][key]
7168
end
7269
end
7370
end
@@ -78,6 +75,5 @@ def id_for_option value
7875
return "#{@tag_attributes[:id]}_#{value}"
7976
end
8077

81-
8278
end
8379
end

app/concepts/matestack/ui/core/form/textarea/textarea.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
require_relative '../utils'
2+
require_relative '../has_errors'
23
module Matestack::Ui::Core::Form::Textarea
34
class Textarea < Matestack::Ui::Core::Textarea::Textarea
45
include Matestack::Ui::Core::Form::Utils
6+
include Matestack::Ui::Core::Form::HasErrors
57

68
requires :key
79
optional :multiple, :init, for: { as: :input_for }, label: { as: :input_label }
810

911
def response
1012
label text: input_label if input_label
1113
textarea html_attributes.merge(attributes: vue_attributes)
12-
span class: 'errors', attributes: { 'v-if': error_key } do
13-
span class: 'error', text: '{{ error }}', attributes: { 'v-for': "error in #{error_key}" }
14-
end
14+
render_errors
1515
end
1616

1717
def vue_attributes

app/concepts/matestack/ui/core/form/utils.rb

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,6 @@ def input_key
44
"data['#{key.to_s}']"
55
end
66

7-
def error_key
8-
"errors['#{key.to_s}']"
9-
end
10-
117
def input_wrapper
128
case input_for
139
when nil

docs/components/form.md

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -647,7 +647,7 @@ end
647647

648648
### Example 4: Multiple input fields of different types
649649

650-
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', 'textarea' types!
650+
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!
651651

652652
On our example page, we define the input fields, together with a `type: X` configuration:
653653

@@ -662,7 +662,6 @@ class ExamplePage < Matestack::Ui::Page
662662
form_input id: 'password-input', key: :password_input, type: :password
663663
form_input id: 'number-input', key: :number_input, type: :number
664664
form_input id: 'range-input', key: :range_input, type: :range
665-
form_input id: 'textarea-input', key: :textarea_input, type: :textarea
666665
form_submit do
667666
button text: 'Submit me!'
668667
end
@@ -822,6 +821,53 @@ end
822821

823822
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!
824823

824+
#### Customizing error rendering
825+
826+
Error messages are rendered in a span with class 'error' wrapped by another span with class 'errors'. You can customize the error rendering or deactivate it on a per component basis.
827+
828+
For the above example the correspoding html looks like:
829+
```html
830+
<span class="errors">
831+
<span class="error">seems to be invalid</span>
832+
</span>
833+
```
834+
835+
###### Deactivate default error rendering
836+
837+
Passing `errors: false` to a form input, select, textarea etc. will disable the default error rendering.
838+
839+
```ruby
840+
form_input key: :foo, type: :text, errors: false
841+
```
842+
843+
##### Customizing default rendering
844+
845+
You can customize errors by passing `errors` with a hash.
846+
Customize the error tag by including a `tag` key with a symbol representing a html tag and the error class by including a `class`
847+
key with a string.
848+
849+
```ruby
850+
form_input key: :foo, type: :text, errors: { tag: :div, class: 'my-error' }
851+
```
852+
Outputs errors as:
853+
```html
854+
<span class="errors">
855+
<div class="my-error">seems to be invalid</div>
856+
</span>
857+
```
858+
859+
By providing a `wrapper` key inside the errros hash you can customize the wrapper the same way you can customize the errors.
860+
861+
```ruby
862+
form_input key: :foo, type: :text, errors: { wrapper: { tag: :div, class: 'my-errors-wrapper' } }
863+
```
864+
Outputs errors as:
865+
```html
866+
<div class="my-errors-wrapper">
867+
<span class="error">seems to be invalid</span>
868+
</div>
869+
```
870+
825871
### Example 9: Mapping the form to an Active Record Model
826872

827873
To test the mapping of our form to an Active Record Model, we make sure our `TestModel`'s description can't be empty:

0 commit comments

Comments
 (0)