-
Notifications
You must be signed in to change notification settings - Fork 355
Support for Rails 5.1 form_with
#369
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 58 commits
4255abb
d8c3c1d
04388cf
47e90fa
2fe6f28
fd2dc57
77ed9ff
edac96a
7d4043e
16d82d6
4c83ea2
5d4dec3
41e1c79
7fbe027
ace7723
33ea133
ce707de
9a45e86
f9ebdec
efd1122
637248a
75a9c39
c9fca28
17259d1
1a1e4ec
fb2c3e0
4e1664b
c2e8473
db3ed98
35606bb
8f2973d
d0f81bf
300c8cf
88858be
7881fbe
6642309
62904ab
4f89877
855764f
fa3fd46
749b297
71b8237
1087a94
fcb71ca
d02e1b7
84c0591
f8f18c3
287d2a4
606ee0d
e55246b
6b9d6f7
855e948
72e1aac
2607af9
710cf10
1b15bb5
ac7d2bc
f315716
14c74a4
f766d0b
69e76ab
e86b7f9
7f97507
102cc54
fc2db34
df5dd0d
0991706
2855154
f85f628
b01a5f4
b81c30b
af121f3
4a028c5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -13,7 +13,7 @@ Bootstrap v4-style forms into your Rails application. | |
| ## Requirements | ||
|
|
||
| * Ruby 2.3+ | ||
| * Rails 5.0+ | ||
| * Rails 5.0+ (Rails 5.1+ for `bootstrap_form_with`) | ||
| * Bootstrap 4.0.0-beta.3+ | ||
|
|
||
| ## Installation | ||
|
|
@@ -40,6 +40,138 @@ Then require the CSS in your `application.css` file: | |
|
|
||
| ## Usage | ||
|
|
||
| ### Rails >= 5.1 | ||
|
||
|
|
||
| To get started, just use the `bootstrap_form_with` helper | ||
| in place of `form_with`. Here's an example: | ||
|
|
||
| ```erb | ||
| <%= bootstrap_form_with(model: @user, local: true) do |f| %> | ||
| <%= f.email_field :email %> | ||
| <%= f.password_field :password %> | ||
| <%= f.check_box :remember_me %> | ||
| <%= f.submit "Log In" %> | ||
| <% end %> | ||
| ``` | ||
|
|
||
| This generates: | ||
|
|
||
| ```html | ||
| <form role="form" action="/users" accept-charset="UTF-8" method="post"> | ||
| <input name="utf8" type="hidden" value="✓" /> | ||
| <div class="form-group"> | ||
| <label class="required" for="user_email">Email</label> | ||
| <input class="form-control" type="email" value="steve@example.com" name="user[email]" /> | ||
| </div> | ||
| <div class="form-group"> | ||
| <label for="user_password">Password</label> | ||
| <input class="form-control" type="password" name="user[password]" /> | ||
| <small class="form-text text-muted">A good password should be at least six characters long</small> | ||
| </div> | ||
| <div class="form-check"> | ||
| <label class="form-check-label" for="user_remember_me"> | ||
| <input name="user[remember_me]" type="hidden" value="0" /> | ||
| <input class="form-check-input" type="checkbox" value="1" name="user[remember_me]" /> Remember me</label> | ||
| </div> | ||
| <input type="submit" name="commit" value="Log In" class="btn btn-secondary" data-disable-with="Log In" /> | ||
| </form> | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is duplication. Should be sufficient to to say that
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. "Stand-alone" |
||
| ``` | ||
|
|
||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it is sufficient to stop here, with a note that says that both
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Back in September, when I was first trying my fork with |
||
| If your form is not backed by a model, use `bootstrap_form_with` like this: | ||
|
|
||
| ```erb | ||
| <%= bootstrap_form_with url: '/subscribe', local: true do |f| %> | ||
| <%= f.email_field :email, value: 'name@example.com' %> | ||
| <%= f.submit %> | ||
| <% end %> | ||
| ``` | ||
|
|
||
| ```html | ||
| <form role="form" action="/subscribe" accept-charset="UTF-8" method="post"> | ||
| <input name="utf8" type="hidden" value="✓" /> | ||
| <div class="form-group"> | ||
| <label for="email">Email</label> | ||
| <input value="name@example.com" class="form-control" type="email" name="email" /> | ||
| </div> | ||
| <input type="submit" name="commit" value="Save " class="btn btn-secondary" data-disable-with="Save " /> | ||
| </form> | ||
| ``` | ||
|
|
||
| #### Important Differences Between `form_with` and `form_for` | ||
|
||
|
|
||
| Rails 5.1 introduced `form_with`, | ||
| which unifies the functionality previously found in `form_for` and `form_tag`. | ||
| `form_for` and `form_tag` will be deprecated in a future version of Rails, | ||
| so new applications should use `bootstrap_form_with`. | ||
|
|
||
| `form_with` is different compared to `form_for` and `form_tag`. | ||
| `bootstrap_form_width` basically just wraps `form_with` | ||
| and adds some functionality, | ||
| and so the different behaviour of `form_with` | ||
| is reflected in `bootstrap_form_with` | ||
| compared to `bootstrap_form_for` | ||
| and `bootstrap_form_tag`. | ||
|
|
||
| ##### Ajax by Default | ||
| `form_with` defaults to submitting forms via Javascript XHR calls, | ||
| like `form_for` or `form_tag` would do if you specified `remote: true`. | ||
| If you want the browser to submit the request | ||
| the same way `form_for` and `form_tag` would do by default, | ||
| you need to specify `local: true` as an option to `form_with`. | ||
|
|
||
| ##### No Default DOM IDs | ||
|
||
| When used with the builder (variable) yielded by `form_with`, | ||
| the Rails field helpers do not generate a default DOM id. | ||
| Because `bootstrap_form_width` just wraps and adds some functionality | ||
| to `form_with`, | ||
| the `bootstrap_form_with` field helpers also do not generate a default DOM id. | ||
| This affect how labels work in your application, | ||
| and may affect automated testing if you're using Capybara or similar tools, | ||
| and you wrote actions or tests that selected on the DOM id of an element. | ||
|
|
||
| For Rails 5.1, you can specify the id explicitly in most cases: | ||
|
|
||
| ```erb | ||
| <%= bootstrap_form_with(model: @user, local: true) do |f| %> | ||
| <%= f.email_field :email %> | ||
| <%= f.password_field :password, id: :password %> | ||
| <%= f.submit "Log In" %> | ||
| <% end %> | ||
| ``` | ||
|
|
||
| generates: | ||
|
|
||
| ```html | ||
| <form role="form" action="/users" accept-charset="UTF-8" method="post"> | ||
| <input name="utf8" type="hidden" value="✓" /> | ||
| <div class="form-group"> | ||
| <label class="required" for="user_email">Email</label> | ||
| <input class="form-control" type="email" value="steve@example.com" name="user[email]" /> | ||
| </div> | ||
| <div class="form-group"> | ||
| <label for="password">Password</label> | ||
| <input id="password" class="form-control" type="password" name="user[password]" /> | ||
| <small class="form-text text-muted">A good password should be at least six characters long</small> | ||
| </div> | ||
| <input type="submit" name="commit" value="Log In" class="btn btn-secondary" data-disable-with="Log In" /> | ||
| </form> | ||
| ``` | ||
|
|
||
| ##### Use `fields` Instead Of `fields_for` In Nested Forms | ||
| For nested forms, use `fields` instead of `fields_for`. | ||
|
|
||
| ##### No Default Classes | ||
| Finally, `bootstrap_form_with` doesn't attach a default class | ||
| to the form. | ||
| If you attached styling to the DOM class that `form_for` added to the form element, | ||
| you'll have to add your own code to attach the appropriate class. | ||
|
|
||
| #### Nested Forms with `bootstrap_form_with` | ||
|
|
||
| This hasn't been tested yet. | ||
|
|
||
| ### Rails < 5.1 | ||
|
|
||
|
||
| To get started, just use the `bootstrap_form_for` helper. Here's an example: | ||
|
|
||
| ```erb | ||
|
|
@@ -84,6 +216,14 @@ If your form is not backed by a model, use the `bootstrap_form_tag`. Usage of th | |
| <% end %> | ||
| ``` | ||
|
|
||
| ### Future Compatibility | ||
|
|
||
| The Rails team has suggested that `form_for` and `form_tag` | ||
| may be deprecated and then removed in future versions of Rails. | ||
| `bootstrap_form` will continue to support | ||
| `bootstrap_form_for` and `bootstrap_form_tag` | ||
| as long as Rails supports `form_for` and `form_tag`. | ||
|
||
|
|
||
| ## Form Helpers | ||
|
|
||
| This gem wraps the following Rails form helpers: | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -9,9 +9,9 @@ class FormBuilder < ActionView::Helpers::FormBuilder | |
| attr_reader :layout, :label_col, :control_col, :has_error, :inline_errors, :label_errors, :acts_like_form_tag | ||
|
|
||
| FIELD_HELPERS = %w{color_field date_field datetime_field datetime_local_field | ||
| email_field month_field number_field password_field phone_field | ||
| range_field search_field telephone_field text_area text_field time_field | ||
| url_field week_field} | ||
| email_field month_field number_field password_field phone_field | ||
| range_field search_field telephone_field text_area text_field time_field | ||
| url_field week_field} | ||
|
|
||
| DATE_SELECT_HELPERS = %w{date_select time_select datetime_select} | ||
|
|
||
|
|
@@ -194,7 +194,7 @@ def collection_check_boxes_with_bootstrap(*args) | |
| options[:multiple] = true | ||
| check_box(name, options, value, nil) | ||
| end | ||
| hidden_field(args.first,{value: "", multiple: true}).concat(html) | ||
| hidden_field(args.first, {value: "", multiple: true}).concat(html) | ||
| end | ||
|
|
||
| bootstrap_method_alias :collection_check_boxes | ||
|
|
@@ -234,7 +234,10 @@ def form_group(*args, &block) | |
| end | ||
|
|
||
| def fields_for_with_bootstrap(record_name, record_object = nil, fields_options = {}, &block) | ||
| fields_options, record_object = record_object, nil if record_object.is_a?(Hash) && record_object.extractable_options? | ||
| if record_object.is_a?(Hash) && record_object.extractable_options? | ||
| fields_options = record_object | ||
| record_object = nil | ||
| end | ||
| fields_options[:layout] ||= options[:layout] | ||
| fields_options[:label_col] = fields_options[:label_col].present? ? "#{fields_options[:label_col]}" : options[:label_col] | ||
| fields_options[:control_col] ||= options[:control_col] | ||
|
|
@@ -245,6 +248,8 @@ def fields_for_with_bootstrap(record_name, record_object = nil, fields_options = | |
|
|
||
| bootstrap_method_alias :fields_for | ||
|
|
||
| # TODO: Add `fields_with_bootstrap` method and alias | ||
|
|
||
| private | ||
|
|
||
| def horizontal? | ||
|
|
@@ -300,11 +305,11 @@ def required_attribute?(obj, attribute) | |
| end | ||
|
|
||
| has_presence_validator = target_validators.include?( | ||
| ActiveModel::Validations::PresenceValidator) | ||
| ActiveModel::Validations::PresenceValidator) | ||
|
|
||
| if defined? ActiveRecord::Validations::PresenceValidator | ||
| has_presence_validator |= target_validators.include?( | ||
| ActiveRecord::Validations::PresenceValidator) | ||
| ActiveRecord::Validations::PresenceValidator) | ||
| end | ||
|
|
||
| has_presence_validator | ||
|
|
@@ -313,6 +318,7 @@ def required_attribute?(obj, attribute) | |
| def form_group_builder(method, options, html_options = nil) | ||
| options.symbolize_keys! | ||
| html_options.symbolize_keys! if html_options | ||
| options[:id] = html_options[:id] if html_options && @options[:skip_default_ids] | ||
|
||
|
|
||
| # Add control_class; allow it to be overridden by :control_class option | ||
| css_options = html_options || options | ||
|
|
@@ -356,11 +362,11 @@ def form_group_builder(method, options, html_options = nil) | |
| label_text ||= options.delete(:label) | ||
| end | ||
|
|
||
| form_group_options.merge!(label: { | ||
| form_group_options[:label] = { | ||
| text: label_text, | ||
| class: label_class, | ||
| skip_required: options.delete(:skip_required) | ||
| }) | ||
| } | ||
| end | ||
|
|
||
| form_group(method, form_group_options) do | ||
|
|
@@ -369,13 +375,19 @@ def form_group_builder(method, options, html_options = nil) | |
| end | ||
|
|
||
| def convert_form_tag_options(method, options = {}) | ||
| options[:name] ||= method | ||
| options[:id] ||= method | ||
| unless @options[:skip_default_ids] | ||
| options[:name] ||= method | ||
| options[:id] ||= method | ||
| end | ||
| options | ||
| end | ||
|
|
||
| def generate_label(id, name, options, custom_label_col, group_layout) | ||
| options[:for] = id if acts_like_form_tag | ||
| # id is the caller's options[:id] at the only place this method is called. | ||
| # The options argument is a small subset of the options that might have | ||
| # been passed to generate_label's caller, and definitely doesn't include | ||
| # :id. | ||
| options[:for] = id if acts_like_form_tag || id | ||
|
||
| classes = [options[:class]] | ||
| classes << (custom_label_col || label_col) if get_group_layout(group_layout) == :horizontal | ||
| unless options.delete(:skip_required) | ||
|
|
@@ -392,7 +404,6 @@ def generate_label(id, name, options, custom_label_col, group_layout) | |
| else | ||
| label(name, options[:text], options.except(:text)) | ||
| end | ||
|
|
||
| end | ||
|
|
||
| def generate_help(name, help_text) | ||
|
|
@@ -461,6 +472,5 @@ def get_help_text_by_i18n_key(name) | |
| help_text | ||
| end | ||
| end | ||
|
|
||
| end | ||
| end | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍