I use the client-side-validation gem and its simple_form plugin for client-side validation. I have a custom model with ActiveModel::Validations included and use simple_form to fill it.
<%= simple_form_for #solution, :validate => true, :url => solve_problem_path, :html => { :class => 'form-horizontal' } do |f| %>
<% #solution.fields.each do |label| %>
<%= f.input label, :validate => { :presence => true } %>
<% end %>
<div class="form-actions">
<%= f.submit 'Solve', :class => 'btn btn-primary' %>
</div>
<% end %>
Custom model:
class Solution < OpenStruct
include ActiveModel::Validations
validate do
#table.each do |key, val|
errors.add(key, :blank) if val.blank?
end
end
def fields
#table.keys
end
end
But client-side-validation doesn't work (server-side does).
Related
I'm trying to let a user create Exercises with Equipment and Muscles in many-to-many relationships through their respective join tables( exercise_equipment, exercise_muscles ). I've gotten the form working for adding one equipment/muscle per exercise, but cannot figure out how to add a link to add another field to the form on the fly.
I've checked out RailsCasts, this post, have asked it as a side question on a previous post of my own, but simply cannot get this functionality to work. I'm fairly new to Rails 4 and am still trying to learn Javascript, but I'd love a thorough explanation of how to set this up the Rails 4 way!
My Models:
# id :integer
# name :string
# is_public :boolean
Exercise
has_many :exercise_equipment
has_many :equipment, :through => :exercise_equipment
accepts_nested_attributes_for :exercise_equipment
# id :integer
# exercise_id :integer
# equipment_id :integer
# optional :boolean
ExerciseEquipment
belongs_to :exercise
belongs_to :equipment
accepts_nested_attributes_for :equipment
# id :integer
# name :string
Equipment
has_many :exercise_equipment
has_many :exercises, :through => :exercise_equipment
My Controller Methods:
def new
#exercise = Exercise.new
#exercise.exercise_equipment.build
#exercise.exercise_muscles.build
end
def create
exercise = current_user.exercises.new( exercise_params )
if exercise.save!
redirect_to exercise
else
render 'new'
end
end
views/exercises/new.html.erb
<h1>Create New Exercise</h1>
<%= form_for #exercise do |f| %>
<%= render 'form', f: f %>
<%= f.submit "New Exercise" %>
<% end %>
views/exercises/_form.html.erb
<%= f.label :name %><br />
<%= f.text_field :name, autofocus: true %>
<%= f.check_box :is_public %> Public
<%= f.fields_for :exercise_muscles do |emf| %>
<%= emf.collection_select :muscle_id, Muscle.all, :id, :name, { include_hidden: false } %>
<% end %>
<%= f.fields_for :exercise_equipment do |eef| %>
<%= eef.collection_select :equipment_id, Equipment.all, :id, :name, { include_hidden: false } %>
<%= eef.check_box :optional %> Optional
<% end %>
Ajax
Cannot figure out how to add a link to add another field to the form
on the fly
The "Rails way" of doing that is to "pull" a new instance of the fields_for block from an ajax request.
The reason why Ajax is recommended is because it's the "Rails way" to do it - completely modular & extensible:
#config/routes.rb
resources :exercises do
get :add_field, on: :collection
end
#app/models/exercise.rb
Class Exercise < ActiveRecord::Base
...
def self.build #-> allows you to call a single method
exercise = self.new
exercise.exercise_equipment.build
exercise.exercise_muscles.build
return
end
end
#app/controllers/exercises_controller.rb
Class ExercisesController < ApplicationController
def add_field
#exercise = Exercise.build
respond_to do |format|
format.html
format.js
end
end
end
#app/views/exercises/new.html.erb
<%= form_for #exercise do |f| %>
<%= render "fields", locals: { f: f } %>
<%= f.submit %>
<% end %>
#app/views/exercises/add_field.js.erb
$("#form_element").append("<%=j render "exercises/form", locals: { exercise: #exercise } %>");
#app/views/exercises/_form.html.erb
<%= form_for exercise do |f| %>
<%= render "fields", locals: { f: f } %>
<% end %>
#app/views/exercises/_fields.html.erb
<%= f.fields_for :exercise_muscles, child_index: Time.now.to_i do |emf| %>
<%= emf.collection_select :muscle_id, Muscle.all, :id, :name, { include_hidden: false } %>
<% end %>
<%= link_to "New Field", exercises_add_fields_path, remote: :true %>
<%= f.fields_for :exercise_equipment, child_index: Time.now.to_i do |eef| %>
<%= eef.collection_select :equipment_id, Equipment.all, :id, :name, { include_hidden: false } %>
<%= eef.check_box :optional %> Optional
<% end %>
<%= link_to "New Field", exercises_add_fields_path, remote: :true %>
This will give you the ability to create the fields through an ajax call; which is the correct way to do it
After attempting to use Rich's solution, I wanted to find one that was a bit more minimal in terms of the code used. I found the gem Cocoon, which works great and was very simple to integrate.
My main _form view:
<div class="form-group">
<%= f.label :name %><br />
<%= f.text_field :name, autofocus: true %>
</div>
<div class="form-group">
<%= f.check_box :is_public %> Public
</div>
<div class="form-group">
<%= f.fields_for :exercise_muscles do |emf| %>
<%= render 'exercise_muscle_fields', :f => emf %>
<% end %>
<%= link_to_add_association 'Add Muscle', f, :exercise_muscles %>
</div>
<div class="form-group">
<%= f.fields_for :exercise_equipment do |eef| %>
<%= render 'exercise_equipment_fields', :f => eef %>
<% end %>
<%= link_to_add_association 'Add Equipment', f, :exercise_equipment %>
</div>
As can be seen here, the addition of a simple "link_to_add_association" method takes care of all of the Javascript in the background. For future readers, here are the partials that each of these form-groups contain:
_exercise_muscle_fields:
<%= f.collection_select :muscle_id, Muscle.all.order( 'muscle_group_id ASC' ), :id, :name, { include_hidden: false } %>
_exercise_equipment_fields:
<%= f.collection_select :equipment_id, Equipment.all.order( 'name ASC' ), :id, :name, { include_hidden: false } %>
<%= f.check_box :optional %> Optional
I want to pass the selected value from the drop down to fullcalendar plugin and the rails form
The select tag
<%= form_tag appointments_path, :html => {:id => "form-1"} do %>
<%= select_tag(:worker_id, options_from_collection_for_select(#client.workers, :id, :name), :selected => #a, :style=>"width:170px;", :prompt => "Select Staff Member")%>
<% end %>
I am passing the #a variable by ajax to
<script>
$('#worker_id').change(function (e) {
var a = parseInt($(this).val());
alert(a);
$.ajax({
type: "GET",
url:"/customers/new",
data : { id: a },
success:function(result){
$('#content1').html("<%= escape_javascript(render :partial =>'form', :locals => ????) %>");
}
});
$('#calendar').fullCalendar('destroy');
RenderCalendar($(this).val());
});
</script>
I am not sure whether i am doing it in right way. I want to pass the value in form which is here:
<%= form_for(#customer) do |f| %>
<%#= f.error_notification %>
<div class="field">
<%#= f.label :Service_Name %>
<%#= f.collection_select :service_id, Category.where(:client_id => #client).order(:name), :services, :name, :id, :service_name %>
</div>
<br /> <br />
<%= f.fields_for :appointments do |builder|%>
<fieldset>
<% if #a!=0 %>
<%= builder.hidden_field :worker_id, :value=> #customer.worker_id %>
<%= builder.hidden_field :client_id, :value=> #client.id%>
<%= builder.label :price %>
<%= builder.text_field :price %>
<%= builder.label :Service_Name %>
<%= builder.collection_select(:service_id, #a.order(:service_name), :id, :service_name, :include_blank => true, :multiple => true ) %>
<% end %>
<%= builder.label :appointment_date %>
<%= builder.date_select :appointment_date %> <br />
<%= builder.label :appointment_start_time %>
<%= builder.time_select :appointment_start_time, ampm: true %> <br />
<%= builder.label :appointment_end_time %>
<%= builder.time_select :appointment_end_time, ampm: true %>
</fieldset>
<%end%>
<div class="field">
<%= f.label :title %>
<%= f.text_field :title %>
</div>
<div class="field">
<%= f.hidden_field :worker_id, :value=>#customer.worker_id %>
</div>
<div class="form-actions">
<%= f.button :submit, :class=>"btn btn-primary" %>
</div>
<% end %>
If I understand correctly: you are trying to rerender the fullcalender jquery plugin with your ajax success callback based on the user selected from the select tag here:
<%= form_tag appointments_path, :html => {:id => "form-1"} do %>
<%= select_tag(:worker_id, options_from_collection_for_select(#client.workers, :id, :name), :selected => #a, :style=>"width:170px;", :prompt => "Select Staff Member") %>
<% end %>
There is an inherent issue with the way you're trying to do this. Any embedded ruby in your views will run only once at runtime. Essentially, your escape_javascript(render partial..... will not run because any embedded ruby is done running at that point.
What you could do is keep the ajax call you already have but run the render partial code in your '/customers/new', instead of trying to call it in the ajax callback -which is after runtime
render :partial =>'form', :locals => ????
This will return the partial code with which you want to then place on the page. With your ajax, simply place it on the page something like this:
success:function(result){
$('#content1').html(result);
}
I have been trying for a while to implement a dynamic dependent form using AJAX in rails 4.0. I have a bike model which has_one make and model. It also has_many quotes. The goal is to have the second form's collection field (model) populate after the first field make is selected.
The form loads correctly and the AJAX call once the first field is selected is made and returns the appropriate set of data (confirmed via puts in the console). However, I am unable to figure out how to then render the partial appropriately. I am getting the error below. I understand there is no f variable once the partial is rendered via the AJAX call but how do I go about designing the form without the f variable?
ActionView::Template::Error (undefined local variable or method `f' for #<#<Class:0x007f95b0fc2068>:0x007f95b54e2490>):
1: <%= f.label :name, 'Model' %>
2: <% if !current_models.blank? %>
3: <%= f.collection_select :name, current_models.collect{ |m| [m.name,m.id]}, :id , :name, include_blank: true %>
4: <% else %>
app/views/shared/_model_questions_fields.html.erb:1:in `_app_views_shared__model_questions_fields_html_erb__894239665582553972_70140482609500'
app/controllers/quotes_controller.rb:70:in `update_model_select'
quotes/new.html.erb
<%= form_for #quote do |f| %>
<%= f.fields_for :bikes do |builder| %>
<p><%= render 'shared/bike_questions_fields', :f => builder %></p>
<% end %>
<%= f.submit "Show Quote", class: "btn btn-large btn-primary" %>
<% end %>
'shared/bike_questions_fields'
<%= f.fields_for :makes do |builder| %>
<p><%= render 'shared/make_questions_fields', :f => builder %></p>
<% end %>
<%= f.fields_for :models do |builder| %>
<div id="bikeModels"
<p><%= render 'shared/model_questions_fields', :f => builder, :current_models => [] %></p>
</div>
<% end %>
'shared/model_questions_fields'
<%= f.label :name, 'Model' %>
<% if !current_models.blank? %>
<%= f.collection_select :name, current_models.collect{ |m| [m.name,m.id]}, :id , :name, include_blank: true %>
<% else %>
<%= f.collection_select :name, [], :id , :name, include_blank: true %>
<% end %>
in quotes controller:
def update_model_select
models = Model.where(:make_id=>params[:id]).order(:name) unless params[:id].blank?
render :partial => "shared/model_questions_fields", :locals => { :current_models => models }
end
in quotes.js.coffee:
ready = ->
jQuery ($) ->
# when the #make field changes
$("#quote_bikes_makes_name").change ->
# make a POST call and replace the content
make = $("select#quote_bikes_makes_name :selected").val()
make = "0" if make is ""
jQuery.get "/quotes/update_model_select/" + make, (data) ->
$("#bikeModels").html data
false
$(document).ready(ready)
$(document).on('page:load', ready)
I solved this by changing 'shared/model_questions_fields' to:
<%= form_for('quote', remote: true) do |f| %>
<%= f.fields_for :bikes do |g| %>
<%= g.fields_for :models do |i| %>
<%= i.label :name, 'Model' %>
<% if !current_models.blank? %>
<%= i.collection_select :name, current_models, :id , :name, include_blank: true %>
<% else %>
<%= i.collection_select :name, [], :id , :name, include_blank: true %>
<% end %>
<% end %>
<% end %>
<% end %>
I need to allow a user to type up a comment and submit before asking them to provide account details. Once these details are provided, they are used to create a new user and the comment they wrote is also submitted and belongs to this new user. Currently, I simply can't figure out how to trigger the submit action on both forms, passing all the information to the server to carry these tasks out.
Basically what I want:
User writes up a comment, submits
Submit button displays a contact info form
User fills this out, submits
Data from both forms are submitted
What I have:
_feedback.html.erb
<%= simple_form_for [#post, Comment.new] do |f| %>
<%= f.input :body, :label => false, :input_html => { :maxlength => '500', :rows => '4', :placeholder => '...'} %>
<% if current_user.guest != true %>
<%= f.button :submit, "Submit" %>
<% end %>
<% end %>
<% if current_user.guest == true %>
<input type="submit" value="Submit" onclick="contactDisplay()"></input>
<% end %>
show.html.erb
<% if current_user.guest == true %>
<div id="contact" class="post-contact">
<%= simple_form_for(#user, :html => { :multipart => true }, defaults: { label: false, required: true }) do |f| %>
<%= f.input :name, :input_html => { :placeholder => 'Your Name', :id => 'name'} %>
<%= f.input :email, :input_html => { :placeholder => 'Your Email', :id => 'email'} %>
<% end %>
<input type="submit" value="Submit" onclick="submitContact()"></input>
</div>
<% end %>
<%= render 'posts/show/feedback' %>
posts.js
function contactDisplay(){
var contact = document.getElementById('contact');
contact.style.display='block';
}
function submitContact(){
document.getElementById("new_comment").submit();
document.getElementById("new_user").submit();
}
You can use nested forms :) Here is a railscasts on how to do it
http://railscasts.com/episodes/196-nested-model-form-part-1
I got this code for a small calculator:
<%= form_tag calculators_path, :method => :post, :remote => true do %>
<%= text_field_tag :data %> = <span id="sum"></div>
<% end %>
Can someone tell me how i can add the JS/Ajax onchange command to it, so the calculator automatically shows the result?
Sorry for that newbish question, im a total beginner.
:)
<%= form_tag calculators_path, :method => :post, :remote => true do %>
<%= text_field_tag :data, nil, :onkeyup => "this.form.sub.click()" %> = <span id="sum"></span>
<%= submit_tag "ok", :name => "sub" %>
<% end %>