Rails: Best way to conditionally show/hide a form field? - ruby-on-rails

I have a model Document which has attributes kind and context. Kind is an integer that's used as as enum (using the excellent active_enum gem). Context only applies to documents of kind '2', i.e. if a document is any kind other than 2, context will be blank.
So in the form page to create a new Document, I have a <select> to choose the kind, and a textarea for the context, which is initially hidden:
<%= form_for #document do |f| %>
...
<%= f.text_area :context, placeholder: 'Context', style: 'display:none' %>
<%= f.select :kind, Document.active_enum_for(:kind).to_select %>
...
<% end %>
And the textarea is shown and hidden using jQuery's show() and hide() methods in a function that's bound to the change() event on the dropdown.
So far, so good. But on the edit page for documents, I don't want the context textarea to always be hidden on the initial page load, because we may be editing a document of kind 2. So I want the textarea to initially be shown if we're editing a document of kind 2, but hidden in other cases.
Here's what I have now:
<% if #document.kind == 2 %>
<%= f.text_area :context, placeholder: 'Context' %>
<% else %>
<%= f.text_area :context, placeholder: 'Context', style: 'display:none' %>
<% end %>
Is there not a better way of doing this? This feels a bit wordy and redundant to me; is there no way I can just have one call to f.text_area and include the style: option conditionally?
Or am I overthinking this?

Use this :
<%= f.text_area :context, placeholder: 'Context', style: "#{'display:none' if #document.kind == 2}" %>
or you can add a css class for this,
display-none{
display:none;
}
<%= f.text_area :context, placeholder: 'Context', class: "#{'display-none' if #document.kind == 2}" %>
Thanks

or you can try javascript ? it's a way to reduce the duplicate code, if you have many fields to hide, this way has some advantages.
<%= f.text_area :context, placeholder: 'Context' %>
<script type="text/javascript">
$(function(){
if ($('#document_kind').val() != '2')
$('#document_context').hide();
});
</script>

Related

Rails 6.0.1 - Trix Editor Parameters not transmitted from form

The added or changed data from a Trix editor field is not submitted/ transmitted.
<%= form_with(model: #comment, local: true) do |form| %>
<div class="form-group">
<h4>Titel</h4>
<%= form.text_field :header, class: "form-control border-top-0 border border-right-0 border-left-0 rounded-0 p-0" %>
<br/>
<h4>Inhalt</h4>
<%= form.trix_editor :comment %>
<%= form.text_field :tonie_id, value: tonie_id, hidden: true %>
<%= form.text_field :user_id, value: current_user.id, hidden: true %>
<%= form.check_box :private %> privater Kommentar
<br/>
<%= form.submit "speichern ",class: 'btn btn-success' %>
</div>
<% end %>
looking into my logs I get the following:
Parameters: {"authenticity_token"=>"xxxx", "tcomment"=>{"header"=>"title", "comment"=>"", "tonie_id"=>"49", "user_id"=>"1", "private"=>"0"}, "commit"=>"speichern "}
Any idea, what could be wrong?
This has been discussed here: https://github.com/rails/rails/issues/37399
You can fix by manually assigning ids to each of the rich text fields. Like this below:
form.rich_text_area :first_description, id: 'trix_first_description'
form.rich_text_area :second_description, id: 'trix_second_description'
form.rich_text_area :third_description, id: 'trix_third_description'
Hope this helps.
This may happen if there are multiple forms of the same model type on your page. I also encountered the same problem. I think it may be caused by duplicate ID.
When multiple rich text editors of the same model type appear on the same page, only the first one can be used normally. After other windows input content, they will be filled into the hidden tags of the first window, so when submitting , The data is null
Sorry i don't know how to express my thoughts in english, hope you understand my speech

Rails require radio button from a collection

I need a way for my form to not be sent if the user didn't bother to select any radio buttons.
I'd like to to that within the view and the controller, not in the model (the data shouldn't even be sent)
<%= form_tag("/bookings/new", method: "get") do %>
<% #flights.each do |flight| %>
<%= radio_button_tag :flight_id, flight.id %>
<% end %>
<%= submit_tag "book now" %>
<% end %>
edit, to clarify
normally I'd do
<%= f.text_field :name, required: true %>
but, as I have many radio buttons and I only need one for the form to work, I don't know how to implement it
You can set validation in the model to see the presence of checkbox if javascript is disabled. This is a more robust method.
validates :flight_id, :acceptance => true
Docs here - http://guides.rubyonrails.org/active_record_validations.html#acceptance
Edit
function validateCheckBox() {
var x = document.getElementById("flight_id").checked;
if(!x) {alert("Not checked")}
}
<%= submit_tag "book now" , :onclick => "validateCheckBox();" %>
<%= f.text_field :name, required: true %>
This still works perfectly for radio buttons, and it's okay if it ends up on all radio items. The form will still only require one input.
I just tested it on my Rails 6 app.

validation for form_tag in ruby on rails

I am working on Ruby 2 and rails 4. I want to add html validation 'required' inside the form_tag of Rails.My codes are below.
<%= form_tag file_download_contacts_path, :method => :post, :id => 'contact-form', :class => 'special' do |f| %>
<div><%= label_tag :Name %><br />
<%= text_field_tag "contact[name]" , nil, placeholder: "Your Name" %></div>
<div><%= label_tag :Email %><br />
<%= email_field_tag "contact[email]" , nil, placeholder: "your#mail.com" %></div>
<div><%= label_tag :Mobile %><br />
<%= telephone_field_tag "contact[phone]" , nil, placeholder: "Your Contact No" %></div>
<div><br /><%= button_tag 'Submit' %></div>
<% end %>
click on submit button it directly goes to file_download_contacts_path page though the text areas are blank. How can I prevent this? In Html we can use 'required'. Please help me if any body can. Thank you.
Something like this should help you
$(document).ready(function () {
$(“#contact-form”).validate({
debug: true,
rules: {
“contact[email]“: {required: true, email: true},
“contact[name]“: {required: true, name: true},
“contact[phone]“: {required: true, phone: true}
}
});
});
Source
Rails cannot do any form validation before it's submitted, since the validation needs to happen in the client (and rails is of course server-side). You need to use javascript. If you google "javascript form validation" you will see a tremendous amount of information.
jQuery is a very popular javascript library which has various form validation plugins, google and have a look.
Note: Above answers are all correct and perfect with simple explanation, but in case if you want to show an actual error message to the user, (In case you want to a complex validation), then below answer is for you,
Here In my scenario I have two checkbox in my form, and I want to validate that at least one checkbox should be clicked, if not then I will show a error message to the user.
<%= form_tag( add_to_telegram_path, method: :post, id: 'add-to-telegram-form') do |t| %>
Select channel
<%= check_box_tag "channel_names[]", id: 'channel_1'%> Channel 1
<%= check_box_tag "channel_names[]", id: 'channel_2'%> Channel 2
<%= button_tag "Send", :class => 'btn btn-success', id: 'add-to-telegram-btn', onclick: "validTelegram()" %>
<%end%>
Then in JS,
function validTelegram() {
event.preventDefault();
var checkboxes = Array.from(document.getElementsByName("channel_names[]"));
if(checkboxes.reduce((acc, curr) => acc || curr.checked, false)){
$('#add-to-telegram-form').submit();
} else {
//swal('Error','Please select atleast one channel!','warning');
alert('Some error!')
}
}
I called a JS function on the submit click of form_tag, (If you are using sibmit_tag replace it with button_tag)
I have prevented default behaviour of the submit button
Then I started to get the elements by its Id/Class or by Name, and checked what ever I want to do in the JS, and then based on my validation Either I am calling submit() on the form ID, or showing the error message.
Note: For showing error message I have used Sweet alert, you can stick with the alert();, but in case If you are looking for better UI, then uncomment my swal() code in JS and comment out alert();
You can read more about Sweet Alert PopUp

create form fields on the fly from drop down

I dint even understand how to search for my problem scenario in google.
Problem : When a user selects from a drop-down, the below form fields should change accordingly.
Example : If a user selects "Human" from Drop-down , then below form_fields should be shown like "age" field ,"ethnicity" field etc..
If a user selects "Event" from drop-down, below form fields should show "Date", "venue" etc..
and By default we will have "SUBMIT" button.
Can anyone tell how to achieve this ? Any help with simple_form ruby gem also would be helpful.
You can do this with some simple javascript. Lets say you have a form with the following fields
<%= f.select :some_field, %w[Human Cat], {}, {:class => 'my-select'} %>
<div class="human">
<%= f.label :age %><br>
<%= f.text_field :age %>
</div>
<div class="cat" style="display:none;">
<%= f.label :sub_species %><br>
<%= f.text_field :sub_species %>
</div>
Now add some javascript to make it dynamic
<script>
$(".my-select").change(function(){
if($(".my-select").val() == "Human"){
$(".human").css('display', 'block');
$(".cat").css('display', 'none');
}else{
$(".human").css('display','none');
$(".cat").css('display', 'block');
}
})
</script>
This is quite easy to achieve with the simple_form gem and some simple javascript.
View
<%= simple_form_for #object do |f| %>
<%= f.input :attribute_name, :collection => ['Human', 'Event'], :input_html => { :class => 'dropdown' %>
<div class="toggle human" style="display:none;">
<%= f.input :age %>
</div>
<div class="toggle event" style="display:none;">
<%= f.input :date %>
</div>
<%= f.button :submit %>
<% end %>
The key here is wrapping the fields you want to toggle in div with specific selectors.
Then we look for a change event on the dropdown field, and toggle our divs accordingly.
Javascript
$('.dropdown').change(function() {
// hide divs first
$('form .toggle').hide();
// get value from dropdown
var divClass = $('.dropdown').val().toLowerCase();
// show necessary div
$('.' + divClass).show();
});
Using this approach, you don't need to write the specific case for every value of the dropdown, but can use the lower case value of the dropdown as the selector for the div.
Here is a simple example with static markup.

How to use one partial in different contexts

i am new to rails and try to do the following:
I would like, because i use Bootstrap, to have a partial for a input field, with it's label and a little icon i called symbol in this case.
Here is my view:
<%= form_for(#user, :class => "form-horizontal" ) do |f| %>
<fieldset>
<%= render 'shared/text_field', function: f, tag: :name, symbol: '<i class="icon-user"></i>' %>
<%= render 'shared/text_field', function: f, tag: :email, symbol: "#" %>
<%= render 'shared/password_field', function: f, tag: :password, symbol: '<i class="icon-log"></i>' %>
<%= render 'shared/password_field', function: f, tag: :password_confirmation, alt: "Passwort wiederholen", symbol: '<i class="icon-log"></i>' %>
<!-- SUBMIT -->
<%= f.submit "Anmeldung", :class => "btn btn-primary" %>
</fieldset>
<% end %>
Here a subpartial for normal input fields:
<%= render 'shared/bootstrap/input_field' %>
<% content_for :label do %>
<%= function.label tag, :class => "control-label", :for => "prependedInput" %>
<% end %>
<%content_for :symbol do %>
<%= raw(symbol) %>
<% end %>
<% content_for :field do %>
<%= function.text_field tag, :class => "input-xlarge", :id => "prependedInput", :size => "6" %>
<% end %>
(there is also a subpartial for password fields, basicly exchanging function.text_field with function.input_field)
And here is the input_field which is rendered:
<div class="control-group">
<%= yield :label %>
<div class="controls">
<div class="input-prepend">
<span class="add-on"><%= yield :symbol %> </span>
<%= yield :field %>
</div>
</div>
</div>
So my question is: how can i solve this Problem in a nice and easy way (this things happend while refactoring and it got even worse then better) and how can i make it work, because by now something like this happens: http://pastebin.com/bNsgT9AR so yield puts with each content_for the content and the content before into the place (except the last one)
It would be great to hear nice solutions from you, there has to be a so much nicer way as almost always in Rails.
Greetings form Germany ;)
As the author of The Rails View, I agree with Ben Taitelbaum's post on this and say that Formtastic is totally a great option for this kind of complexity.
With regards to the code you posted, as developers we want to avoid that kind of view writing across the board, as it ends up an unmanageable mess. There's too many files involved in editing a form, and at some point, some other dev will come in and overwrite our shared partial, not knowing where else it was used, to change the way a different form behaves. Any benefit of code reuse that we can get out of this kind of abstraction is completely obliterated by the potential for it to go sour very quickly with mutliple developers.
While Formtastic is great, I've also been using SimpleForm a lot in my recent apps, especially when I don't need the full power of Formtastic.
SimpleForm will handle your label, and it also supports i18n as well. With this it would be:
<%= simple_form_for #user do |f| %>
<%= f.input :name, :input_html => { :class => 'user-icon' } %>
<%= f.input :email %>
<%= f.input :password, :as => :password, :input_html => { :class => 'log-icon' } %>
<%= f.input :password_confirmation, :as => :password, :input_html => { :class => 'log-icon' } %>
<%= f.button :submit %>
<% end %>
Then in your I18n file, you can have:
en:
simple_form:
labels:
user:
username: 'User name'
password: 'Password'
password_confirmation: 'Password confirmation'
de:
simple_form:
labels:
user:
username: 'Benutzername'
password: 'Passwort'
password_confirmation: 'Passwort wiederholen'
And more as you need it. You can define your hints and placeholders in the i18n file as well.
Also, think about adding the icon as a CSS pseudo class :after, and do so for each class that you need to add in the code)
input.user-icon:after {
// image as background, positioned properly, etc etc
}
input.log-icon:after {
// image as background, positioned properly, etc etc
}
And that way, you can remove this kind of stuff from your ERB/HTML and put it where it belongs: In the CSS (presentation layer).
There's a great section on forms in The Rails View, where they recommend using FormBuilders for this type of thing (they also discuss formtastic, which is worth checking out).
The biggest lesson I got out of this book (and I can't recommend it enough!) is that it's important to keep views as simple as possible, since they tend to be the least fun part of a rails app to debug, especially as the app grows large. So from this perspective, I would want the view to be nice and simple, something like:
<%= semantic_form_for(#user) do |f| %>
<%= f.inputs do %>
<%= f.input :name, class: 'icon-user' %>
<%= f.input :email %>
<%= f.input :password %>
<%= f.input :password_confirmation %>
<% end %>
<%= f.buttons %>
<% end %>
Start with a nice readable view as the goal, and add the necessary helpers and formbuilders as needed for custom field types.
I'd recommend using I18n for setting the submit button text to be "Anmeldung" instead of hardcoding that as well.
I haven't used it, but you might want to check out formtastic-bootstrap for getting the integration you want..
I feel that's a bit too much generalization overhead for a simple input field.
Have you considered simply creating one shared input field partial and just passing in the field name (you named it tag) and the type of field (text_field or password_field) as parameters? I can see that your approach might be more general, but if you just need those two options for now, keep it as simple as possible.
Addition in reply to comment:
You can simply pass parameters to your partial like so:
<%= render 'shared/error_messages', object: f.object %>
And then use the the variables in your partial. in your case you could
<%= render 'shared/bootstrap/input_field', function: f, tag: :name, input_field_type: :text_field %>
and then in _input_field.html.erb
<div class="control-group">
<%= function.label tag, :class => "control-label", :for => "prependedInput" %>
...

Resources