Rails 3: How to add/remove a group of fields using Jquery? - javascript

I'm trying to figure out how to add and remove a group of fields in my Rails 3 app using Jquery. I got the jquery script to work with plain html, which you can see here: http://jsfiddle.net/beehive/ABvFH/1/
However, I can't figure out how to translate this to Rails 3. More specifically, what do I replace "formfield" with in the application.js file for this to work?
application.js
$(document).ready(function() {
$(function() {
var x = $('#uploadform');
var i = $('#uploadform ul').size() + 1;
$('#addmore').live('click', function() {
if ($(".item", x).length < 9) {
$('<ul class="item" class="field">formfield ' + i + ' Remove</p>').appendTo(x);
i++;
}
return false;
});
$('#remfields').live('click', function() {
if (i > 2) {
$(this).parents('ul').remove();
i--;
}
return false;
});
});​
});
form.html.erb
<%= form_for(#upload) do |f| %>
<% if #upload.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#upload.errors.count, "error") %> prohibited this upload from being saved:</h2>
<ul>
<% #upload.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<h2>Add More Fields</h2>
<div id="uploadform">
<ul class="field">
<li>
<%= f.label :title %><br />
<%= f.text_field :title %>
</li>
<li>
<%= f.label :genre %><br />
<%= f.collection_select :genre, Upload::GENRES, :to_s, :to_s, :include_blank => true %>
</li>
<li>
<%= f.label :category %><br />
<%= f.collection_select :category, Upload::CATEGORIES, :to_s, :to_s, :include_blank => true %>
</li>
<li>
<%= f.label :age %><br />
<%= f.collection_select :age, Upload::AGES, :to_s, :to_s, :include_blank => true %>
</li>
</ul>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>

All I had to do to get it to work is comment $(function() {
$(document).ready(function() {
//$(function() {
var x = $('#uploadform');
var i = $('#uploadform ul').size() + 1;
$('#addmore').live('click', function() {
if ($(".item", x).length < 9) {
$('<ul class="item" class="field">formfield ' + i + ' Remove</p>').appendTo(x);
i++;
}
return false;
});
$('#remfields').live('click', function() {
if (i > 2) {
$(this).parents('ul').remove();
i--;
}
return false;
});
//});​
});
Edit:
What do you mean insert them?
To access the elements you can loop through them with
$('#uploadform > ul').each(function(index) {
alert(index + ': ' + $(this).text());
});
Notice your generated HTML. You are creating ul (unordered list) for each li (list item). you may want to fix that to only append list items.

I guess you want to allow user to add many uploads before submit form.
In that case I'm using rails nested attributes.
Here is example. Order has many Photos
Order model:
class Order < ActiveRecord::Base
has_many :photos, :dependent=>:destroy, :inverse_of=>:order
accepts_nested_attributes_for :photos, :allow_destroy=>true,:reject_if=> :all_blank
end
Photo model:
class Photo < ActiveRecord::Base
has_attached_file :photo
belongs_to :order
end
Order form view:
<%= form_for #order, :html => {:autocomplete => :off, :multipart => true} do |f| %>
<%= f.label :title %><br />
<%= f.text_field :title %>
<h3>Order photos</h3>
<%= f.fields_for :photos do |photo| %>
<%= render :partial=>'photo_form', :object=> photo, :as => :f %>
<% end %>
<%= link_to 'Add photo', new_photo_path, :remote=>true, :id=>'add_photo' %>
<%= f.submit%>
<% end %>
photo_form partial:
<fieldset class="photo" data-status="<%= f.object.persisted? ? 'db':'new' %>">
<% if f.object.persisted? %>
<div class="field">
<label>Photo:</label><br />
<%= link_to f.object.photo.url, :target=>'_blank' do %><%= image_tag f.object.photo.url(:mini) %><% end %>
</div>
<% else %>
<%= f.label :photo
<%= f.file_field :photo %>
<%= f.text_field :description %>
<%= f.check_box :_destroy, :class=>'delete' %>
</fieldset>
Now in your controller you need 3 actions to edit order with photos: edit, update, add_photo(ajax action when user click add photo link):
class OrdersController < ApplicationController
def edit
#order = Order.find(params[:id])
end
# Action responsible for updating existing order record
def update
#order = Order.find(params[:id])
if #order.update_attributes(params[:order])
redirect_to #order, :notice=>'Order updated'
else
render :action => "edit" #save error, display order form with errors
end
end
# Action responsible for creating nested form for order photo
def new_photo
#nothing just renders 'new_photo.js.erb'
end
end
new_photo.js.erb:
$object=$("<%= escape_javascript( render(:partial => "photo_form", :locals=>{
:f=>FormBuilder.new("order[photos_attributes][#{rand(400000)}]",Photo.new(),self,{},proc {})
}) ) %>").hide(); //generates new photo view
$object.insertBefore("a#add_photo").slideDown(500);
This is just example, but you shouldn't have problem to adopt this in your application.

Related

Rails 3 Nested Model Form with Javascript

I have created a nested model form for Lessons to Questions and Answers. It was not picking up the coffee script so I have add it the coffee code as Javascript but i am get an error and Add Question is not working. Anyone and assist please.
Lesson.rb
has_many :questions
accepts_nested_attributes_for :questions, :allow_destroy => true
Question.rb
belongs_to :lesson
has_many :answers
accepts_nested_attributes_for :answers, :allow_destroy => true
Answer.rb
belongs_to :question
application_helper.rb
def link_to_add_fields(name, f, association) new_object = f.object.send(association).klass.new
id = new_object.object_id
fields = f.fields_for(association, new_object, :child_index => id) do |builder|
render(association.to_s.singularize + "_fields", :f => builder)
end
link_to(name, '#', :class => "add_fields", :data => {:id => id, :fields => fields.gsub("\n", "")}) end
admin/lessons/_form.html.erb
<%= form_for([:admin,#lesson]) do |f| %>
<% if #lesson.errors.any? %>
<div class="notification error png_bg">
<img src="/assets/admin/icons/cross_grey_small.png" title="Close this notification" alt="close" />
<div>
<h2><%= pluralize(#lesson.errors.count, "error") %></h2>
<ul>
<% #lesson.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
</div>
<% end %>
<label class="formlabel">Lesson Title</label>
<%= f.text_field :title %>
<%= f.fields_for :questions do |builder| %>
<%= render 'question_fields', :f => builder %>
<% end %>
<%= link_to_add_fields "Add Question", f, :questions %>
<%= f.submit 'Save', :class => 'button' %>
_question_fields.html.erb
<fieldset>
<%= f.label :content, "Question" %><br />
<%= f.text_area :content %><br />
<%= f.check_box :_destroy %>
<%= f.label :_destroy, "Remove Question" %>
<%= f.fields_for :answers do |builder| %>
<%= render 'answer_fields', :f => builder %>
<% end %>
<%= link_to_add_fields "Add Answer", f, :answers %>
</fieldset>
_answer_fields.html.erb
<fieldset>
<%= f.label :content, "Answer" %>
<%= f.text_field :content %>
<%= f.check_box :_destroy %>
<%= f.label :_destroy, "Remove Answer" %>
</fieldset>
Here is the javascript I have added just before the end of the the head tag. I am getting an error of "$('form').on is not a function. (In '$('form').on', '$('form'....
<script>
jQuery(function() {
$('form').on('click', '.remove_fields', function(event) {
$(this).prev('input[type=hidden]').val('1');
$(this).closest('fieldset').hide();
return event.preventDefault();
});
return $('form').on('click', '.add_fields', function(event) {
var regexp, time;
time = new Date().getTime();
regexp = new RegExp($(this).data('id'), 'g');
$(this).before($(this).data('fields').replace(regexp, time));
return event.preventDefault();
});
});
</script>
Jquery .on() function needs at least jquery 1.7 to work. Update your jquery or use .bind() in place of .on().

How to Create Nested Forms in Rails 4

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

Confirmation message - Javascript rails application

I'm a bit new to rails and I'm just trying to display a confirmation message when a "Proposition" (created model) has been inserted. For this, I'm using a javascript functionality embedded to rails.
Unfortunately, when I click and submission button, the proposition is correctly inserted but I can't find a way to display this confirmation message (like "proposition correctly submitted" for example)...
Below the code that I'm using :
Proposition Model :
class Proposition < ActiveRecord::Base
belongs_to :ad
attr_accessible :email, :name, :phone, :price
validates_presence_of :name or :price or :email or :phone
end
Create method in controller :
def create
#ad = Ad.find(params[:ad_id])
#proposition = #ad.propositions.create(params[:proposition])
respond_to do |format|
if #proposition.save
format.html { redirect_to ad_path(#ad), notice: 'Proposition was successfully created.' }
format.json { render json: ad_path(#ad), status: :created, location: #proposition }
format.js
else
format.html { render action: "new" }
format.json { render json: #proposition.errors, status: :unprocessable_entity }
format.js { render action: "new" }
end
end
end
_form.html.erb :
<%= form_for [#ad, #ad.propositions.build], :remote => true do |f| %>
<% if #proposition.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#proposition.errors.count, "error") %> prohibited this proposition from being saved:</h2>
<ul>
<% #proposition.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :Prénom %><br />
<%= f.text_field :name %>
</div>
<div class="field">
<%= f.label :"Adresse mail" %><br />
<%= f.text_field :email %>
</div>
<div class="field">
<%= f.label :Téléphone %><br />
<%= f.text_field :phone %>
</div>
<div class="field">
<%= f.label :"Prix proposé" %><br />
<%= f.text_field :price %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
create.js.erb :
$('#new_proposition').fadeOut(1000);
new.js.erb :
$('#new_proposition_link').hide().after('<%= j render("form") %>');
Thanks for your help !
Since you already have a create.js.erb file, you can use JS to display an alert message or insert a confirmation message into the page like so,
# app/views/.../create.js.erb
$('#new_proposition').before('<p>Proposition correctly submitted!</p>');
$('#new_proposition').fadeOut(1000);
This inserts the <p>Proposition correctly submitted!</p> before the #new_proposition element. You may want to insert it somewhere else since this element will disappear.

How to pass id from javascript to rails forms

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);
}

how to include the javascript into dropdown list in ruby on rails?

I have a javascript file for the cascading dropdown boxes loading from different models. But I dont know how to include into the dropdown list.
Form:
<% content_for :javascript do %>
var master_surveys = <%=
Condition::MasterSurvey.all.map {|ms| {id: ms.Master_Survey_Code, to_s: ms[:Master_Survey_Name]}}.to_json.html_safe
%>
var elements = <%= elements = Hash.new { |hash, code| hash[code] = [] }
Condition::Element.all.each {|e| elements[e.Master_Survey_Code] << {id: e.Element_Code, to_s: e.Element} }.to_json.html_safe
%>
var sub_elements = <%= sub_elements = Hash.new { |hash, code| hash[code] = [] }
Condition::SubElement.all.each {|s| sub_elements[s.Element_Code] << {id: s.Sub_Element_Code, to_s: s.Sub_Element} }.to_json.html_safe
%>
var materials = <%=
materials = Hash.new { |hash, code| hash[code] = [] }
Condition::RenewSchedule.all.each {|rs| materials[rs.Sub_Element_Code] << {id: rs.Material_Code, to_s: rs.Material} }.to_json.html_safe
%>
$(document).ready(function(){
$('select#enr_rds_surv_rdsap_xref_master_survey').chainedTo('select#enr_rds_surv_rdsap_xref_Element_Code');
});
<% end %>
<%= form_for(#enr_rds_surv_rdsap_xref) do |f| %>
<% if #enr_rds_surv_rdsap_xref.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#enr_rds_surv_rdsap_xref.errors.count, "error") %>:</h2>
<ul>
<% #enr_rds_surv_rdsap_xref.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :Master_Survey %><br/>
<%= f.select :master_survey, Condition::MasterSurvey.all.map{|e| [e.Master_Survey_Code]}, { :prompt => 'Please Select' } %>
</div>
<div class="field">
<%= f.label :Element_Code %><br/>
<%= f.select :Element_Code, Condition::Element.all.map{|e| [e.Element, e.Element_Code]}, { :prompt => 'Please Select' } %>
</div>
<div class="field">
<%= f.label :Sub_Element_Code %><br/>
<%= f.select :Sub_Element_Code, Condition::SubElement.all.map{|e| [e.Sub_Element, e.Sub_Element_Code]}, { :prompt => 'Please Select' } %>
</div>
<div class="field">
<%= f.label :Material_Code %><br/>
<%= f.select :Material_Code, Condition::RenewSchedule.all.map{|e| [e.Material]}, { :prompt => 'Please Select' } %>
</div>
<div class="actions">
<%= f.submit 'Save'%>
</div>
<% end %>
So, The above javascript file collect the data from the parent. In the form, I created a dropdown list statically loaded the data from the database. I want to include the javascript to loaded automatically into the dropdown list dynamically.
Thanks in advance!!!!
I usually do it by Ajax and partials(or page, up to you.), not nice but might be help?
Bind change even on your parent selects. like
$("#parent_select").change(function(){
$.ajax({
url: '/parents/'+$(this).val()+'/childs/',
complete: function(data){
$('#children_select').html(data);
}
})
});
And in your child controller render a partial(or page) with only contents of options of selected children. like:
<%children.each do |child|%>
<option value='<%=child.id%>'><%=child.name%></option>
<%end%>

Categories