optimism 0.2.10
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitbook/assets/alien_science.svg +1 -0
- data/.gitbook/assets/fill_forms.svg +1 -0
- data/.gitbook/assets/high_five.svg +1 -0
- data/.gitbook/assets/loading.svg +1 -0
- data/.gitbook/assets/master_plan.svg +1 -0
- data/.gitbook/assets/setup.svg +1 -0
- data/.gitbook/assets/special_event.svg +1 -0
- data/.gitbook/assets/success_factors.svg +1 -0
- data/.gitbook/assets/super_woman.svg +1 -0
- data/.gitbook/assets/web_developer.svg +1 -0
- data/.gitignore +10 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +178 -0
- data/LICENSE.txt +21 -0
- data/README.md +52 -0
- data/Rakefile +3 -0
- data/SUMMARY.md +9 -0
- data/advanced-usage.md +52 -0
- data/bin/console +15 -0
- data/bin/loc +3 -0
- data/bin/setup +8 -0
- data/bin/standardize +4 -0
- data/lib/optimism.rb +108 -0
- data/lib/optimism/railtie.rb +11 -0
- data/lib/optimism/rake.rb +18 -0
- data/lib/optimism/version.rb +3 -0
- data/lib/templates/optimism_channel.js +11 -0
- data/lib/templates/optimism_channel.rb +5 -0
- data/optimism.gemspec +31 -0
- data/quick-start.md +123 -0
- data/reference.md +165 -0
- data/setup.md +45 -0
- data/typical-usage.md +102 -0
- metadata +189 -0
data/bin/console
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "optimism"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "pry"
|
14
|
+
|
15
|
+
Pry.start
|
data/bin/loc
ADDED
data/bin/setup
ADDED
data/bin/standardize
ADDED
data/lib/optimism.rb
ADDED
@@ -0,0 +1,108 @@
|
|
1
|
+
require "cable_ready"
|
2
|
+
require "optimism/version"
|
3
|
+
require "optimism/railtie" if defined?(Rails)
|
4
|
+
|
5
|
+
module Optimism
|
6
|
+
include CableReady::Broadcaster
|
7
|
+
class << self
|
8
|
+
mattr_accessor :channel, :form_class, :error_class, :disable_submit, :suffix, :emit_events, :add_css, :inject_inline, :container_selector, :error_selector, :form_selector, :submit_selector
|
9
|
+
self.channel = "OptimismChannel"
|
10
|
+
self.form_class = "invalid"
|
11
|
+
self.error_class = "error"
|
12
|
+
self.disable_submit = false
|
13
|
+
self.suffix = ""
|
14
|
+
self.emit_events = false
|
15
|
+
self.add_css = true
|
16
|
+
self.inject_inline = true
|
17
|
+
self.container_selector = "#RESOURCE_ATTRIBUTE_container"
|
18
|
+
self.error_selector = "#RESOURCE_ATTRIBUTE_error"
|
19
|
+
self.form_selector = "#RESOURCE_form"
|
20
|
+
self.submit_selector = "#RESOURCE_submit"
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.configure(&block)
|
24
|
+
yield self
|
25
|
+
end
|
26
|
+
|
27
|
+
def broadcast_errors(model, attributes)
|
28
|
+
return unless model&.errors&.messages
|
29
|
+
resource = model.class.to_s.downcase
|
30
|
+
form_selector, submit_selector = Optimism.form_selector.sub("RESOURCE", resource), Optimism.submit_selector.sub("RESOURCE", resource)
|
31
|
+
attributes = case attributes
|
32
|
+
when ActionController::Parameters, Hash, ActiveSupport::HashWithIndifferentAccess
|
33
|
+
attributes.to_h.keys
|
34
|
+
when String, Symbol
|
35
|
+
[attributes.to_s]
|
36
|
+
when Array
|
37
|
+
attributes.flatten.map &:to_s
|
38
|
+
else
|
39
|
+
raise Exception.new "attributes must be a Hash (Parameters, Indifferent or standard), Array, Symbol or String"
|
40
|
+
end
|
41
|
+
process_resource(model, attributes, [resource])
|
42
|
+
if model.errors.any?
|
43
|
+
cable_ready[Optimism.channel].dispatch_event(name: "optimism:form:invalid", detail: {resource: resource}) if Optimism.emit_events
|
44
|
+
cable_ready[Optimism.channel].add_css_class(selector: form_selector, name: Optimism.form_class) if Optimism.form_class.present?
|
45
|
+
cable_ready[Optimism.channel].set_attribute(selector: submit_selector, name: "disabled") if Optimism.disable_submit
|
46
|
+
else
|
47
|
+
cable_ready[Optimism.channel].dispatch_event(name: "optimism:form:valid", detail: {resource: resource}) if Optimism.emit_events
|
48
|
+
cable_ready[Optimism.channel].remove_css_class(selector: form_selector, name: Optimism.form_class) if Optimism.form_class.present?
|
49
|
+
cable_ready[Optimism.channel].remove_attribute(selector: submit_selector, name: "disabled") if Optimism.disable_submit
|
50
|
+
end
|
51
|
+
cable_ready.broadcast
|
52
|
+
head :ok
|
53
|
+
end
|
54
|
+
|
55
|
+
def process_resource(model, attributes, ancestry)
|
56
|
+
attributes.each do |attribute|
|
57
|
+
if attribute.ends_with?("_attributes")
|
58
|
+
resource = attribute[0..-12]
|
59
|
+
nested_models = model.send(resource.to_sym)
|
60
|
+
nested_models.each_with_index do |nested, index|
|
61
|
+
process_resource(nested, attributes[attribute][index.to_s], ancestry + [resource, index]) if attributes[attribute].key?(index.to_s)
|
62
|
+
end
|
63
|
+
else
|
64
|
+
process_attribute(model, attribute, ancestry.dup)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def process_attribute(model, attribute, ancestry)
|
70
|
+
resource = ancestry.shift
|
71
|
+
resource += "_#{ancestry.shift}_attributes_#{ancestry.shift}" until ancestry.empty?
|
72
|
+
container_selector, error_selector = Optimism.container_selector.sub("RESOURCE", resource).sub("ATTRIBUTE", attribute), Optimism.error_selector.sub("RESOURCE", resource).sub("ATTRIBUTE", attribute)
|
73
|
+
if model.errors.messages.map(&:first).include?(attribute.to_sym)
|
74
|
+
message = "#{attribute.humanize} #{model.errors.messages[attribute.to_sym].first}#{Optimism.suffix}"
|
75
|
+
cable_ready[Optimism.channel].dispatch_event(name: "optimism:attribute:invalid", detail: {resource: resource, attribute: attribute, text: message}) if Optimism.emit_events
|
76
|
+
cable_ready[Optimism.channel].add_css_class(selector: container_selector, name: Optimism.error_class) if Optimism.add_css
|
77
|
+
cable_ready[Optimism.channel].text_content(selector: error_selector, text: message) if Optimism.inject_inline
|
78
|
+
else
|
79
|
+
cable_ready[Optimism.channel].dispatch_event(name: "optimism:attribute:valid", detail: {resource: resource, attribute: attribute}) if Optimism.emit_events
|
80
|
+
cable_ready[Optimism.channel].remove_css_class(selector: container_selector, name: Optimism.error_class) if Optimism.add_css
|
81
|
+
cable_ready[Optimism.channel].text_content(selector: error_selector, text: "") if Optimism.inject_inline
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
module ActionView::Helpers
|
87
|
+
class FormBuilder
|
88
|
+
def container_for(attribute, **options, &block)
|
89
|
+
@template.tag.div @template.capture(&block), options.merge!(id: container_id_for(attribute)) if block_given?
|
90
|
+
end
|
91
|
+
|
92
|
+
def container_id_for(attribute)
|
93
|
+
Optimism.container_selector.sub("RESOURCE", object_name.delete("]").tr("[", "_")).sub("ATTRIBUTE", attribute.to_s)[1..-1]
|
94
|
+
end
|
95
|
+
|
96
|
+
def error_for(attribute, **options)
|
97
|
+
@template.tag.span options.merge! id: error_id_for(attribute)
|
98
|
+
end
|
99
|
+
|
100
|
+
def error_id_for(attribute)
|
101
|
+
Optimism.error_selector.sub("RESOURCE", object_name.delete("]").tr("[", "_")).sub("ATTRIBUTE", attribute.to_s)[1..-1]
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
class ActionController::Base
|
107
|
+
include Optimism
|
108
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require "fileutils"
|
2
|
+
|
3
|
+
desc "Inject some optimism into this application"
|
4
|
+
task :"optimism:install" do
|
5
|
+
CHANNELS = [
|
6
|
+
{app_path: "app/javascript/channels/optimism_channel.js", template_path: "../templates/optimism_channel.js" },
|
7
|
+
{app_path: "app/channels/optimism_channel.rb", template_path: "../templates/optimism_channel.rb" },
|
8
|
+
]
|
9
|
+
|
10
|
+
CHANNELS.each do |channel|
|
11
|
+
if File.exist?("./#{channel[:app_path]}")
|
12
|
+
$stderr.puts "=> [ skipping ] #{channel[:app_path]} already exists"
|
13
|
+
else
|
14
|
+
FileUtils.cp(File.expand_path(channel[:template_path], __dir__), "./#{channel[:app_path]}")
|
15
|
+
$stderr.puts "=> #{channel[:app_path]} created"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
import consumer from './consumer'
|
2
|
+
import CableReady from 'cable_ready'
|
3
|
+
|
4
|
+
consumer.subscriptions.create('OptimismChannel', {
|
5
|
+
received (data) {
|
6
|
+
if (data.cableReady)
|
7
|
+
CableReady.perform(data.operations, {
|
8
|
+
emitMissingElementWarnings: false
|
9
|
+
})
|
10
|
+
}
|
11
|
+
})
|
data/optimism.gemspec
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
lib = File.expand_path("lib", __dir__)
|
2
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
|
+
require "optimism/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = "optimism"
|
7
|
+
spec.version = Optimism::VERSION
|
8
|
+
spec.authors = ["leastbad"]
|
9
|
+
spec.email = ["hello@leastbad.com"]
|
10
|
+
|
11
|
+
spec.summary = "Drop-in Rails form validations"
|
12
|
+
spec.description = "Realtime remote form input validations delivered via websockets"
|
13
|
+
spec.homepage = "https://github.com/leastbad/optimism"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = Dir.chdir(File.expand_path("..", __FILE__)) do
|
17
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
18
|
+
end
|
19
|
+
spec.bindir = "exe"
|
20
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
21
|
+
spec.require_paths = ["lib"]
|
22
|
+
|
23
|
+
spec.add_development_dependency "bundler", "~> 2.0"
|
24
|
+
spec.add_development_dependency "rake", "~> 13.0"
|
25
|
+
spec.add_development_dependency "pry"
|
26
|
+
spec.add_development_dependency "pry-nav"
|
27
|
+
spec.add_development_dependency "standardrb"
|
28
|
+
spec.add_dependency "rack"
|
29
|
+
spec.add_dependency "rails", ">= 5.2"
|
30
|
+
spec.add_dependency "cable_ready", "~> 4.0.9"
|
31
|
+
end
|
data/quick-start.md
ADDED
@@ -0,0 +1,123 @@
|
|
1
|
+
# Quick Start
|
2
|
+
|
3
|
+
Let's start with the simplest scenario possible: you have a form with multiple input elements, and when the user clicks on the Submit button you want to display any validation error messages beside the elements that have issues. When the user resolves these issues and clicks the Submit button, the form is processed normal and the page navigates to whatever comes next.
|
4
|
+
|
5
|
+
## Model
|
6
|
+
|
7
|
+
Validations are covered in-depth by the [official documentation](https://guides.rubyonrails.org/active_record_validations.html#validation-helpers). Optimism doesn't require anything special.
|
8
|
+
|
9
|
+
{% hint style="info" %}
|
10
|
+
Optimism is designed for ActiveRecord models that have validations defined, although it should work with any Ruby class that implements [Active Model](https://guides.rubyonrails.org/active_model_basics.html) and has an `errors` accessor.
|
11
|
+
{% endhint %}
|
12
|
+
|
13
|
+
## View
|
14
|
+
|
15
|
+
Here's sample form partial for a Post model. It has two attributes - **name** and **body** and was generated with a Rails scaffold command.
|
16
|
+
|
17
|
+
{% code title="app/views/posts/\_form.html.erb BEFORE Optimism" %}
|
18
|
+
```rust
|
19
|
+
<%= form_with(model: post, local: true) do |form| %>
|
20
|
+
<% if post.errors.any? %>
|
21
|
+
<div id="error_explanation">
|
22
|
+
<h2><%= pluralize(post.errors.count, "error") %> prohibited this post from being saved:</h2>
|
23
|
+
|
24
|
+
<ul>
|
25
|
+
<% post.errors.full_messages.each do |message| %>
|
26
|
+
<li><%= message %></li>
|
27
|
+
<% end %>
|
28
|
+
</ul>
|
29
|
+
</div>
|
30
|
+
<% end %>
|
31
|
+
|
32
|
+
<div class="field">
|
33
|
+
<%= form.label :name %>
|
34
|
+
<%= form.text_field :name %>
|
35
|
+
</div>
|
36
|
+
|
37
|
+
<div class="field">
|
38
|
+
<%= form.label :body %>
|
39
|
+
<%= form.text_area :body %>
|
40
|
+
</div>
|
41
|
+
|
42
|
+
<div class="actions">
|
43
|
+
<%= form.submit %>
|
44
|
+
</div>
|
45
|
+
<% end %>
|
46
|
+
```
|
47
|
+
{% endcode %}
|
48
|
+
|
49
|
+
And here is that same form partial, configured to work with Optimism:
|
50
|
+
|
51
|
+
{% code title="app/views/posts/\_form.html.erb AFTER Optimism" %}
|
52
|
+
```rust
|
53
|
+
<%= form_with(model: post) do |form| %>
|
54
|
+
<div class="field">
|
55
|
+
<%= form.label :name %>
|
56
|
+
<%= form.text_field :name %>
|
57
|
+
<%= form.error_for :name %>
|
58
|
+
</div>
|
59
|
+
|
60
|
+
<div class="field">
|
61
|
+
<%= form.label :body %>
|
62
|
+
<%= form.text_area :body %>
|
63
|
+
<%= form.error_for :body %>
|
64
|
+
</div>
|
65
|
+
|
66
|
+
<div class="actions">
|
67
|
+
<%= form.submit %>
|
68
|
+
</div>
|
69
|
+
<% end %>
|
70
|
+
```
|
71
|
+
{% endcode %}
|
72
|
+
|
73
|
+
Eagle-eyed readers will see that setting up a bare-bones Optimism integration requires removing two things and adding one thing to each attribute:
|
74
|
+
|
75
|
+
1. Remove `local: true` from the `form_with` on the first line
|
76
|
+
2. Remove the error messages block from lines 2-12 entirely
|
77
|
+
3. Add an `error_for` helper for each attribute
|
78
|
+
|
79
|
+
The `error_for` helper creates an empty `span` tag with an id such as _posts\_body\_error_, and this is where the error messages for the body attribute will appear.
|
80
|
+
|
81
|
+
{% hint style="success" %}
|
82
|
+
Even though `form_with` is remote-by-default, many developers were confused and frustrated by the lack of opinionated validation handling out of the box for remote forms. Since scaffolds are for new users to get comfortable, remote forms are disabled. This is the primary reason that Optimism was created: we want our tasty remote forms without any heartburn.
|
83
|
+
{% endhint %}
|
84
|
+
|
85
|
+
## Controller
|
86
|
+
|
87
|
+
The last step is to slightly modify the **create** and **update** actions in our PostsController. The other actions have been removed for brevity:
|
88
|
+
|
89
|
+
{% code title="app/controllers/posts\_controller.rb" %}
|
90
|
+
```rust
|
91
|
+
def create
|
92
|
+
@post = Post.new(post_params)
|
93
|
+
respond_to do |format|
|
94
|
+
if @post.save
|
95
|
+
format.html { redirect_to @post, notice: 'Post was successfully created.' }
|
96
|
+
format.json { render :show, status: :created, location: @post }
|
97
|
+
else
|
98
|
+
format.html { broadcast_errors @post, post_params }
|
99
|
+
format.json { render json: @post.errors, status: :unprocessable_entity }
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def update
|
105
|
+
respond_to do |format|
|
106
|
+
if @post.update(post_params)
|
107
|
+
format.html { redirect_to @post, notice: 'Post was successfully updated.' }
|
108
|
+
format.json { render :show, status: :ok, location: @post }
|
109
|
+
else
|
110
|
+
format.html { broadcast_errors @post, post_params }
|
111
|
+
format.json { render json: @post.errors, status: :unprocessable_entity }
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
```
|
116
|
+
{% endcode %}
|
117
|
+
|
118
|
+
The only meaningful change required \(as seen on lines 8 and 20 in this example\) is to replace `render :new` and `render :edit` with a call to `broadcast_errors` which has two mandatory parameters: the model instance and the list of attributes to validate. Usually this is the whitelisted params hash, but you can pass a subset as small as one attribute to be validated.
|
119
|
+
|
120
|
+
That's all there is to it. You now have live - if _unstyled_ - form validations being delivered over websockets.
|
121
|
+
|
122
|
+
![](.gitbook/assets/high_five.svg)
|
123
|
+
|
data/reference.md
ADDED
@@ -0,0 +1,165 @@
|
|
1
|
+
# Reference
|
2
|
+
|
3
|
+
## ActionController Mixins
|
4
|
+
|
5
|
+
### **broadcast\_errors**\(model, attributes\)
|
6
|
+
|
7
|
+
**model**: an instance of an Active Record model, or a class inheriting from Active Model
|
8
|
+
**attributes**: one or many attributes in the form of a ActionController::Parameters, Hash, HashWithIndifferentAccess, Symbol, String or Array \(of Strings or Symbols\)
|
9
|
+
|
10
|
+
Call this method in a resource controller's `create` or `update` actions when a model validation fails. A list of instructions for the client browser will be prepared and dispatched via a persistent websocket connection.
|
11
|
+
|
12
|
+
The two most common use cases are form-based \(Parameters\) or in-line editing of a single attribute \(Symbol or String\).
|
13
|
+
|
14
|
+
#### Example
|
15
|
+
|
16
|
+
```ruby
|
17
|
+
if @post.update(post_params)
|
18
|
+
# Eat. Pray. Love.
|
19
|
+
else
|
20
|
+
broadcast_errors @post, post_params
|
21
|
+
end
|
22
|
+
```
|
23
|
+
|
24
|
+
|
25
|
+
|
26
|
+
## Form Builder Helpers
|
27
|
+
|
28
|
+
### container\_for\(attribute, \*\*options, &block\)
|
29
|
+
|
30
|
+
**attribute**: Symbol identifying the the model attribute to use
|
31
|
+
**options**: an implicit Hash allowing you to pass in class names, data attributes and other HTML attributes
|
32
|
+
**block**: all ERB content passed to this helper inside the do..end will be rendered as content
|
33
|
+
|
34
|
+
Call this helper to create a `div` that will wrap your input element along with any labels and error messages. It will have an id attribute that will allow Optimism to route any validation errors to the correct place. When a validation failure occurs, this `div` will have an _error_ class added to it, allowing a style cascade to change the visual appearance of the input element and the error message.
|
35
|
+
|
36
|
+
#### Example
|
37
|
+
|
38
|
+
```rust
|
39
|
+
<%= form.container_for :name, class: "field" do %>
|
40
|
+
<%= form.text_field :name %>
|
41
|
+
<% end %>
|
42
|
+
```
|
43
|
+
|
44
|
+
|
45
|
+
|
46
|
+
### container\_id\_for\(attribute\)
|
47
|
+
|
48
|
+
**attribute**: Symbol identifying the the model attribute to use
|
49
|
+
|
50
|
+
Returns the id required for a container to wrap your input elements and receive CSS updates. Use it if you are forced to create your own container markup from scratch; generally it is easiest to make use of **container\_for** if possible.
|
51
|
+
|
52
|
+
#### Example
|
53
|
+
|
54
|
+
```rust
|
55
|
+
<blockquote id="<%= form.container_id_for :name %>"></blockquote>
|
56
|
+
```
|
57
|
+
|
58
|
+
|
59
|
+
|
60
|
+
### error\_for\(attribute, \*\*options\)
|
61
|
+
|
62
|
+
**attribute**: Symbol identifying the the model attribute to use
|
63
|
+
**options**: an implicit Hash allowing you to pass in class names, data attributes and other HTML attributes
|
64
|
+
|
65
|
+
Call this helper to create a `span` that you place adjacent to your your input elements. It will have an id attribute that will allow Optimism to route any validation errors to the correct place. When a validation failure occurs, this `span` will have the error message injected into it. It is typically hidden unless there is a message present.
|
66
|
+
|
67
|
+
#### Example
|
68
|
+
|
69
|
+
```rust
|
70
|
+
<%= form.error_for :name, class: "d-none text-danger small form-group" %>
|
71
|
+
```
|
72
|
+
|
73
|
+
|
74
|
+
|
75
|
+
### error\_id\_for\(attribute\)
|
76
|
+
|
77
|
+
**attribute**: Symbol identifying the the model attribute to use
|
78
|
+
|
79
|
+
Returns the id required for a container to receive validation error text. Use it if you are forced to create your own error message markup from scratch; generally it is easiest to make use of **error\_for** if possible.
|
80
|
+
|
81
|
+
```rust
|
82
|
+
<blockquote id="<%= form.error_id_for :name %>"></blockquote>
|
83
|
+
```
|
84
|
+
|
85
|
+
|
86
|
+
|
87
|
+
![](.gitbook/assets/master_plan.svg)
|
88
|
+
|
89
|
+
## Initializer
|
90
|
+
|
91
|
+
Optimism is configurable via an optional initializer file. As with all initializers, changes only take effect after your Rails server has been restarted. Here is a sample initializer file that contains all of the default values for the configuration of the library. All changes apply globally to all instances of Optimism.
|
92
|
+
|
93
|
+
{% code title="config/initializers/optimism.rb" %}
|
94
|
+
```ruby
|
95
|
+
Optimism.configure do |config|
|
96
|
+
config.channel = "OptimismChannel"
|
97
|
+
config.form_class = "invalid"
|
98
|
+
config.error_class = "error"
|
99
|
+
config.disable_submit = false
|
100
|
+
config.suffix = ""
|
101
|
+
config.emit_events = false
|
102
|
+
config.add_css = true
|
103
|
+
config.inject_inline = true
|
104
|
+
config.container_selector = "#RESOURCE_ATTRIBUTE_container"
|
105
|
+
config.error_selector = "#RESOURCE_ATTRIBUTE_error"
|
106
|
+
config.form_selector = "#RESOURCE_form"
|
107
|
+
config.submit_selector = "#RESOURCE_submit"
|
108
|
+
end
|
109
|
+
```
|
110
|
+
{% endcode %}
|
111
|
+
|
112
|
+
**channel**: The ActionCable channel created by the `rake optimism:install` setup task. In most cases, you don't need to change this value. In fact, the only good reason to change this value is if you already have an OptimismChannel. If this describes you, congratulations on your optimism.
|
113
|
+
|
114
|
+
**form\_class**: The CSS class that will be applied to the form if the id has been properly set eg. `posts_form` \(following the simple pattern **resources\_form**\). If form\_class is set to false or nil, no CSS class will be applied.
|
115
|
+
|
116
|
+
**error\_class**: The CSS class the will be applied to a container when a validation fails. Use this class to cascade to the input elements and change their appearance.
|
117
|
+
|
118
|
+
**disable\_submit**: If set to true and your Submit button is named properly eg. `posts_submit` \(following the simple pattern **resources\_submit**\), your Submit button will be disabled if there are validation errors. It will also be re-enabled if the validation errors are corrected. Only use this if you are working with in-line validations or else your users will lose the ability to Submit your form more than once.
|
119
|
+
|
120
|
+
**suffix**: Likely the most important setting of all, this string will be appended to all validation error messages. While most validation errors on the web today do not have a trailing period, this is a matter of developer preference and when you're working on Rails, we care about your trails.
|
121
|
+
|
122
|
+
**emit\_events**: Optimism is so flexible that you can opt to have it fire DOM events in addition to \(or instead of\) text content and CSS updates. Scroll down for more information on the events that will be sent.
|
123
|
+
|
124
|
+
**add\_css**: Flag to control whether containers will receive CSS updates when a form is invalid.
|
125
|
+
|
126
|
+
**inject\_inline**: Flag to control whether validation error messages will be displayed inside _error\_for_ spans.
|
127
|
+
|
128
|
+
**container\_selector**: This is the pattern from which container id CSS selectors will be constructed. You probably shouldn't change this.
|
129
|
+
|
130
|
+
**error\_selector**: This is the pattern from which error\_for span id CSS selectors will be constructed. You probably shouldn't change this.
|
131
|
+
|
132
|
+
**form\_selector**: This is the pattern from which form id CSS selectors will be constructed. You probably shouldn't change this.
|
133
|
+
|
134
|
+
**submit\_selector**: This is the pattern from which Submit button id CSS selectors will be constructed. You probably shouldn't change this.
|
135
|
+
|
136
|
+
|
137
|
+
|
138
|
+
## Events
|
139
|
+
|
140
|
+
If you set the `emit_events` property to true in your initializer, Optimism will emit DOM events in response to validation errors. This can happen in addition to or instead of CSS and text updates. This is a great alternative for complicated integrations where you have legacy components which need to be notified of error conditions on the backend.
|
141
|
+
|
142
|
+
In practical terms, DOM events give you tooling options and creative flexibility that are difficult to achieve with textual error messages and CSS error classes. It's simply a fact that no library can anticipate every design pattern or UI innovation. Today you might connect DOM events to a [toast notification library](https://www.jqueryscript.net/blog/Best-Toast-Notification-jQuery-Plugins.html#vanilla), but tomorrow there could be a mass proliferation of embedded ocular computers with sub-vocalization control interfaces, and we aren't going to tell you how those devices should punish users for bad input.
|
143
|
+
|
144
|
+
### Form-level events
|
145
|
+
|
146
|
+
Event: **optimism:form:invalid**
|
147
|
+
Detail: resource
|
148
|
+
|
149
|
+
Event: **optimism:form:valid**
|
150
|
+
Detail: resource
|
151
|
+
|
152
|
+
One of these events will fire, depending on whether the model is valid. Resource is the pluralized class name of the Active Record model, eg. `posts`
|
153
|
+
|
154
|
+
### Attribute-level events
|
155
|
+
|
156
|
+
Event: **optimism:attribute:invalid**
|
157
|
+
Detail: resource, attribute, text
|
158
|
+
|
159
|
+
Event: **optimism:attribute:valid**
|
160
|
+
Detail: resource, attribute
|
161
|
+
|
162
|
+
One of these events will fire **for each attribute**, depending on whether that attribute is valid. Resource is the pluralized class name of the Active Record model, eg. `posts`. Attribute is hopefully self-explanatory. Text is the text content of the validation error message.
|
163
|
+
|
164
|
+
![](.gitbook/assets/alien_science.svg)
|
165
|
+
|