Rendering HAML using javascript in partial in ERB, Ruby on Rails - javascript

I have started working on a project and the person before me used HAML to display two select boxes and update the second based on the selection of the first. I do not understand how to convert the HAML to ERB so I just put the HAML in a partial. The first select box displays the correct values but the second does not contain anything. How can I make the first box update the second with the correct value? The first one contains states, then the second is supposed to contain installations base on the state selected. Here is my code:
Search view:
<%= render 'state_installation_select' %>
HAML partial:
= state_installation_select #states, #installation_options_by_state # ProgramsHelper
ProgramsHelper:
def state_installation_select(states, installation_options_by_state)
haml_tag(:div, class: 'field') do
haml_concat(label_tag :state, "Please select your state")
haml_concat(select_tag :state, options_for_select(states), include_blank: true, class: 'state state_installation_select')
end
haml_tag(:div, class: 'field') do
haml_concat(label_tag :installation_id, "Please select your installation")
haml_concat(select_tag :installation_id, nil, include_blank: true, class: 'installation state_installation_select')
end
content_for :domready, %(InstallationSelect.init(#{installation_options_by_state.to_json});).html_safe
end
Program Controller:
def search_form_for_parents
#installations = Installation.all
#states = Installation.states
#installations_by_state = #installations.group_by(&:state_code)
#installation_options_by_state = #installations_by_state.each_with_object({}){ |(state,installations), h| h[state] = installations.map{|i| [i.name, i.id]} }
end
Program Javascript:
var InstallationSelect = {
init: function(installations_for_state){
$state_select = $('select.state.state_installation_select');
$state_select.change(function(){
var state = $(this).val();
var installations = installations_for_state[state] || [];
var $installation_select = $(this).parent().parent().find('select.installation.state_installation_select');
$installation_select.html( InstallationSelect.options_for_select(installations) );
}).triggerHandler('change');
},
options_for_select:function(container){
var options = jQuery.map(container, function(c){
return '<option value="' + c[1] + '">' + c[0] + '</option>';
});
return options;
}
};

Related

How can I create dynamic form select in Rails 6 with jquery

I am new to rails and web development. I am trying to create a dynamic form select on my category and subcategory form such that subcategories associated with a selected category will be automatically loaded. Here is is what I have tried but did not work. Whenever I reload the page I page empty drop downlist the subcategories even if I selected an item
ROUTE:
post '/subcategories/find_by_category', to: 'subcategories#find_by_category'
CONTROLLER
class SubcategoriesController < ApplicationController
def show; end
def find_by_category
category = Category.find(params[:category_id])
subcategories = category.subcategories.find_all
render json: {subcategories: subcategories}
end
end
MODEL
class Category < ApplicationRecord
has_many :subcategories
end
class Subcategory < ApplicationRecord
belongs_to :category, required: false
end
VIEW
<div class="field">
<label class="label">Category</label>
<%= f.collection_select :category_id, Category.all, :id, :name %>
</div>
<div class="field">
<label class="label">Sub Category</label>
<%= f.select :subcategory_id, options_for_select([]) %>
</div>
<script>
$(document).ready(function(){
var getSubcategories = function(category_id){
var subcategories = $('#service_subcategory_id');
$($subcategories).empty();
$.service('/subcategories/find_by_category', { category_id: category_id},
function(data){
$.each(data.subcategories, function(index, subcategory){
var option = $('<option />');
option.attr('value', subcategory.id);
option.text(subcategory.name);
option.appendTo($subcategories);
});
})
};
var getSelectedCategory = function(){
return $('#service_category_id').val();
};
$('#service_category_id').change(function() {
var category_id = getSelectedCategory();
getSubcategories (category_id);
});
getSubcategories(getSelectedCategory());
});
</script>
I get this error on the console
readyException.js:6 Uncaught ReferenceError: $subcategories is not defined
at getSubcategories (edit:493)
at HTMLDocument.<anonymous> (edit:520)
at mightThrow (deferred.js:97)
at process (deferred.js:139)
You are calling a different variable than what you have defined.
You wrote:
var subcategories = $('#service_subcategory_id');
$($subcategories).empty();
There are two issues here.
You did not define the variable $subcategories, but you are attempting to reference it.
You are attempting to wrap the $subcategories variable in JQuery, which I think would be redundant with what you are trying to do, as the variable above is already a JQuery object.
You can name the variable starting with $ if that helps you remember it is a JQuery object, but this should get you to the next blocker:
var $subcategories = $('#service_subcategory_id');
$subcategories.empty();

Div is not updated after selecting value on a dropdown list

I'm trying to display a chart upon selecting a state and a city.
The cities dropdown list is populated based on the selection of the state in the first dropdown.
The problem is when I click on a city no chart is displayed, nor updated if I change the selection, and I can't seem to figure out the problem.
I'm using ChartKick to display a pie chart with the values given by a query in the controller.
My code is as follows:
controllers/city_stats_controller.rb:
def city_stats
#state = params[:state]
#cities = City.where(:state => #state).select(:id, :display_name).uniq.order('display_name ASC')
# #city_id = City.select('id').where(:state => params[:city_state]).where(:name => params[:city_name])
#city_id = City.select('id').find_by_id(params[:city])
dynamic_query = ActiveRecord::Base.send(:sanitize_sql_array, ['SELECT COALESCE(name) as name, count(*) FROM (SELECT name, regs.segment_id, count(*) FROM regs
INNER JOIN segments ON regs.segment_id = segments.id
WHERE regs.city_id = ?
GROUP BY segment_id, name
ORDER BY count(*) DESC) as tabe
GROUP BY name;', #city_id])
#spatial_ratio = ActiveRecord::Base.connection.select_all(dynamic_query)
puts #city_id.inspect #I can see the value printed in the terminal
puts #spatial_ratio.inspect #Same here, even after changing the selection
respond_to do |format|
format.json { render :json => #cities }
format.js {
render json: {
html: render_to_string(
partial: 'city_stats',
locals: {
city_id: #city_id,
spatial_ratio: #spatial_ratio
})
}
}
format.html
end
end
views/city_stats/city_stats.html.erb :
<div class="row">
<label>State <span>:</span></label>
<%= collection_select :city, :state, City.select(:state).uniq.order('state ASC'), :state, :state, {:prompt => 'Select a State'}, {id: 'city_state', multiple: false} %>
<label>City <span>:</span></label>
<%= collection_select :city, :name, [], :name, :name, {:prompt => 'Select a City'}, {id: 'city_name', multiple: false} %>
</div>
<div id="cities">
<!-- Here I render the cities list upon selecting a state-->
<%= render :partial => 'city_stats', :object => #cities %>
</div>
<div id="stats">
</div>
<script type="text/javascript">
$(document).ready(function() {
$("#city_state").on('change', function(){
$.ajax({
url: "/admin/city_stats",
type: "GET",
data: {state: $(this).val()},
success: function(data) {
$("#city_name").children().remove();
// Create options and append to the list
var listitems = [];
$.each(data,function(key, value) {
listitems += '<option value="' + value.id + '">' + value.display_name + '</option>';
});
$("#city_name").append(listitems);
//console.log(listitems);
$("#city_name").on('click', function(){
$.ajax({
url: "/admin/city_stats",
type: "GET",
data: {city: $(this).val()},
success: function(data) {
var content = $("#city_name").val()
console.log(content);
$('#stats').replaceWith("<%= j render(partial: 'city_stats') %>");
return content;
}
})
})
}
})
});
});
And in the partial : views/city_stats/_city_stats.html.erb :
<%= pie_chart #spatial_ratio.rows %>
N.B : If I replace the city_id in the dynamic query in the controller by a random id manually, a chart is displayed of the corresponded city when I refresh the page, but I don't know why it's not working dynamically
1.update your city_stats method like below
def city_stats
...
...
respond_to do |format|
format.json { render :json => #cities }
format.js
end
end
2. create city_stats.js.erb file with below code
$("#stats").html("<%= escape_javascript(pie_chart #spatial_ratio.rows) %>");
3. change your city_click event like this
$("#city_name").on('click', function(){
$.ajax({
url: "/admin/city_stats",
type: "GET",
data: {city: $(this).val()},
success: function(data) {
}
})
});
This should solve your problem. If any problem/doubt comment
#city_id = City.select('id').find_by_id(params[:city])
This line doesn't return you a number. It returns an instance of your model with only one field (id) loaded. Try to change it to
#city_id = City.select('id').find_by_id(params[:city]).id
That will give you a number.
Also, as I see, in your handler of AJAX in $("#city_name").on('click') you don't actually update your view with generated chart.
<%= j render(partial: 'city_stats') %>
This line gets evaluated only once, on initial rendering of the html page (that's why everything works when city_id is set manually), so on each AJAX request, that doesn't reload your page entirely, you replace $('#stats') with the same content.
By the way, once you replace $('#stats'), you will not access it the next time without total rerendering of the page, because it gets removed from the DOM. See reference of (replaceWith).
I think that in case of AJAX it's better to use .empty() on $('#stats') container and then .append() new chart inside it.
The idea is that if you want to update chart without reloading of the page, you should use the fresh data from the the server.
success: function(data) {
In your case it will be inside the data variable.
I see that in your controller you have this:
format.js {
render json: {
html: render_to_string(
partial: 'city_stats',
locals: {
city_id: #city_id,
spatial_ratio: #spatial_ratio
})
}
}
So you should use this data in your handler. It might be like this:
success: function(data) {
$('#stats').empty().append(data.html);
}
However, I'm not sure that it's reachable in current implementation. I guess that every time you make an AJAX request, it will end with
format.json { render :json => #cities }
So you should either return both #cities and rendered partial here, and use them in relevant success callbacks, or somehow separate responses for each request.
One more thing about the code - it's not good to bind event handler for $("#city_name") inside change handler of $("#city_state"), because on every change of $("#city_state") you add the same handler for $("#city_name") click, and on each click this handler will be executed as many times as it has been bound. You can use .off before binding to prevent this, but maybe it's worth to do some refactoring.

HTML Checkbox giving all values

I know this question seems repeated but I have searched through many sites and am still unable to get my code working.
I'll explain my problem as I paste the code.
Here is my form:
<form action="">
<% position = #deals_daily_count.size - 2 %>
<% num_of_expired_deals = #deals_daily_count[position].size %>
<% loops = 0 %>
<% while loops < num_of_expired_deals %>
<input type="checkbox" class="choice" name="choice[]" value="<%= #deals_daily_count[position][loops] %>"><%= #deals_daily_count[position][loops] %>
<br/>
<% loops = loops + 1 %>
<% end %>
</form>
My javascript is as shown below:
document.getElementById("add_deal_button").addEventListener("click", function () {
var buttons = $('.choice');
var chart = $('#analytics_line').highcharts();
buttons.each(function () {
var currentBox = $(this);
var isChecked = currentBox.is(':checked');
var wasChecked = currentBox.data("wasChecked");
var choice = $(this).val();
<% loops = 0 %>
<% while loops < num_of_expired_deals %>
var title = "<%= raw #deals_daily_count[position][loops]%>"
if (choice == title) {
<% title_of_deal = #deals_daily_count[position][loops].to_s%>
}
<% loops = loops + 1 %>
<% end %>
<% merchant_id_position = #deals_daily_count.size - 1 %>
<% merchant_id = #deals_daily_count[merchant_id_position] %>
<%= raw data = DealAnalyticService.set_analytics_for_line_graph(merchant_id, title_of_deal, Date.today.beginning_of_month, Date.today) %>
if (!wasChecked && isChecked) {
var series = chart.addSeries({
name: "<%= data[0][0]%>" + " view count",
pointStart: <%= data[0][1] %>,
pointInterval: 24 * 3600 * 1000,
data: <%= data[0][2] %>,
visible: true
});
currentBox.data("series", series);
var series2 = chart.addSeries({
name: "<%= data[0][0]%>" + " redemption count",
pointStart: <%= data[0][1] %>,
pointInterval: 24 * 3600 * 1000,
data: <%= data[0][3] %>,
visible: true
});
currentBox.data("series2", series2);
}
if (wasChecked && !isChecked) {
var series = currentBox.data("series");
series.remove();
var series2 = currentBox.data("series2");
series2.remove();
}
if (isChecked) {
currentBox.data("wasChecked", true);
}
else {
currentBox.data("wasChecked", false);
}
});
});
To explain what I'm doing, I am actually using HighCharts and am trying to populate the data into a series and add it to my chart when checkbox is ticked. When it isn't tick, I will remove it from my graph.
The removing and adding part is working perfectly. Only issue is that when adding a graph, apparently
buttons.each(function (){}
is not working properly. It does not go iterate through every single checkbox and give me the value of that particular checkbox. Instead, I believe it returned me all values of the checkbox. This results in regardless which checkbox is checked, only the last value is used since it goes through my while loop.
I have tried using ("input:checkbox").each(function() {} but it gives me the same result.
I've also tried using isChecked to determine whether to pass the value into choice or pass null but it does not work as well
I hope that someone can explain to me why is this the issue as I've been trying several methods with no success for hours.
Thank you!

jQuery updating rails form partial based on selected item in drop down

In my rails app I have a dropdown menu that a user can select an account to make a payment to:
//_payment.html.erb
<div class="field" id="payment-to-account">
<%= f.label 'Payment To:' %><br>
<%= f.collection_select :to_account_id, #liability_account_payment_list, :id, :account_details, {include_blank: 'Please Select'} %>
</div>
When the user selects a account I render a partial inside of this form based on their selection:
//_payment.html.erb
<%= form_for(#transaction,
as: :transaction,
url:
#transaction.id ? account_transaction_path(#account, #transaction) : account_transactions_path) do |f| %>
...
<% #f = f %>
<div id="to-account-form" style="display:none;"></div>
...
<script>
jQuery(function(){
$("#payment-to-account").change(function() {
var selected_item = $( "#payment-to-account option:selected" ).text();
var selected_item_index = $( "#payment-to-account option:selected" ).index();
//looks for account type in parentheses followed by space moneysign " $"
var regExp = /\(([^)]+)\)\s\$/;
var matches = regExp.exec(selected_item);
// array of account ids in order of list
var payment_account_ids = <%= raw #payment_list_ids %>;
switch (matches[1]) {
case 'Mortgage':
$('#to-account-form').html("<%= j render 'payment_to_mortgage', f: #f %>");
$('#to-account-form').slideDown(350);
break;
case 'PersonalLoan':
case 'CreditCard':
case 'StudentLoan':
case 'OtherLiability':
$('#to-account-form').html("<%= j render 'payment_to_all_other_liabilities', f: #f %>");
$('#to-account-form').slideDown(350);
break;
default:
$('#to-account-form').html("<br>" + "Contact support, an error has occurred");
$('#to-account-form').slideDown(350);
}
});
});
</script>
Right now it renders the correct partial based on the selection, but when that partial loads I need more information from the account model. I created a method called find_pay_to_account that take the input selected account id in the Accounts model that looks for the account based on the id.
When the user selects and account from the drop down, I'd like that method called on the partial that is loaded so I can show the user additional information about the account they are making a payment to before they submit the form. But I don't know how. I wanted to add something like this to my jQuery switch statement.
selected_account_id = payment_account_ids[selected_item_index-1]
#payment_to_account = Account.find_pay_to_account(selected_account_id)
Since rails preloads the partials in the background, making the following change to my partial render in the case statements still wont work:
From this
$('#to-account-form').html("<%= j render 'payment_to_mortgage', f: #f %>");
To this
$('#to-account-form').html("<%= j render 'payment_to_mortgage', f: #f, #payment_to_account: #payment_to_account %>");
I did some searching and found that with AJAX might be able to help:
Pragmatic Studio
Rails Cast
But i'm trying to access the model, not the controller and I'm trying to update a form partial. What is the correct way to do this?
Here are pics that show the user flow. An example of what I'm trying to update can be seen in the last pic. When the mortgage account is selected, it needs to show the minimum payment for the mortgage account. Right now it says zero because the partials rendering with all the information from BOA seed 0214.
If you want to access record information from your model inside of front-end javascript you will indeed want to setup a small api to query the database for that information. In ajax it would be something like this
function processDataFunction(data){
console.log(data)
}
$.ajax({
type: "GET",
dataType: "json",
url: "/some-path/:some_id",
success: processDataFunction(data){}
});
#config/routes.rb
Rails.application.routes.draw do
get "/some-path/:some_id", to: "some_controller#some_view", :defaults => { :format => :json }
end
#app/controllers/some_controller.rb
class SomeController < ApplicationController
def some_view
#some_records = SomeModel.find_by_id(params[:some_id])
respond_to do |format|
format.json { render json: #some_records }
end
end
end
To access the information in the rendered partial without making another controller action, I collected all data I might need in the original action. That way I could get the exact result I was looking for without changing my routes and doing ajax request.
To do this I added methods to the controller new action. You can see from my original question, all accounts I may need information for are in the variable that is in the dropdown menu:
#liability_account_payment_list
This is where the dropdown menu gets its information from
That variable is in the Transaction controller new action. So I created another variable storing an array on the line after the above variable:
#liability_accounts_payment_list_minimum_payments = #liability_account_payment_list.map {|account| account.minimum_payment.to_f + account.minimum_escrow_payment.to_f}
This new variable is an array of all the accounts minimum payments in the order they are listed in the dropdown menu the user will select from.
Then I changed the jQuery on the page to the following
//_payments.html.erb
<script>
jQuery(function(){
$("#payment-to-account").change(function() {
var selected_item = $( "#payment-to-account option:selected" ).text();
var selected_item_index = $( "#payment-to-account option:selected" ).index();
//looks for something in parentheses followed by space moneysign " $"
var regExp = /\(([^)]+)\)\s\$/;
var matches = regExp.exec(selected_item);
// array of minimum payments from accounts in list converted from ruby to js
var min_payments = <%= raw #liability_accounts_payment_list_minimum_payments %>;
// set the js variable to the appropriate minimum payment
var selected_account_min_payment = min_payments[selected_item_index-1];
switch (matches[1]) {
case 'Mortgage':
$('#to-account-form').html("<%= j render 'payment_to_mortgage', f: #f %>");
$("#min-payment-field-hidden").val(selected_account_min_payment);
$("#min-payment-field").html(selected_account_min_payment);
$('#to-account-form').slideDown(350);
break;
case 'PersonalLoan':
case 'CreditCard':
case 'StudentLoan':
case 'OtherLiability':
$('#to-account-form').html("<%= j render 'payment_to_all_other_liabilities', f: #f %>");
$("#min-payment-field-hidden").val(selected_account_min_payment);
$("#min-payment-field").html(selected_account_min_payment);
$('#to-account-form').slideDown(350);
break;
default:
$('#to-account-form').html("<br>" + "Contact support, an error has occurred");
$('#to-account-form').slideDown(350);
}
});
});
</script>
The lines that have min-payment-field-hidden are because setting two different divs with the same id does not work. One div is being used to set hidden_field, the other is showing the user what the value is.
//_payment.html.erb
<-- To make sure the appropriate minimum payment is submitted to controller -->
<%= f.hidden_field :amount, :id => "min-payment-field-hidden" %>
<div>
<%= f.label "Minimum Payment" %>
<div id="min-payment-field"></div>
</div>
If you look at my switch statement, you can see I set the above value with these lines:
$("#min-payment-field-hidden").val(selected_account_min_payment);
$("#min-payment-field").html(selected_account_min_payment);
Now the user can see the minimum payment for the specific account they choose from the dropdown.

How to add and remove nested model fields dynamically using Haml and Formtastic

We've all seen the brilliant complex forms railscast where Ryan Bates explains how to dynamically add or remove nested objects within the parent object form using Javascript.
Has anyone got any ideas about how these methods need to be modified so as to work with Haml Formtastic?
To add some context here's a simplified version of the problem I'm currently facing:
# Teacher form (which has nested subject forms) [from my application]
- semantic_form_for(#teacher) do |form|
- form.inputs do
= form.input :first_name
= form.input :surname
= form.input :city
= render 'subject_fields', :form => form
= link_to_add_fields "Add Subject", form, :subjects
# Individual Subject form partial [from my application]
- form.fields_for :subjects do |ff|
#subject_field
= ff.input :name
= ff.input :exam
= ff.input :level
= ff.hidden_field :_destroy
= link_to_remove_fields "Remove Subject", ff
# Application Helper (straight from Railscasts)
def link_to_remove_fields(name, f)
f.hidden_field(:_destroy) + link_to_function(name, "remove_fields(this)")
end
def link_to_add_fields(name, f, association)
new_object = f.object.class.reflect_on_association(association).klass.new
fields = f.fields_for(association, new_object, :child_index => "new_#{association}") do |builder|
render(association.to_s.singularize + "_fields", :f => builder)
end
link_to_function(name, h("add_fields(this, \"#{association}\", \"#{escape_javascript(fields)} \")"))
end
#Application.js (straight from Railscasts)
function remove_fields(link) {
$(link).previous("input[type=hidden]").value = "1";
$(link).up(".fields").hide();
}
function add_fields(link, association, content) {
var new_id = new Date().getTime();
var regexp = new RegExp("new_" + association, "g")
$(link).up().insert({
before: content.replace(regexp, new_id)
});
}
The problem with implementation seems to be with the javascript methods - the DOM tree of a Formtastic form differs greatly from a regular rails form.
I've seen this question asked online a few times but haven't come across an answer yet - now you know that help will be appreciated by more than just me!
Jack
You're on the right track:
...the DOM tree of a Formtastic form
differs greatly from a regular rails
form.
To adapt Ryan's example for formtastic, it's helpful to be reminded that the semantic_fields_for helper is similar to the semantic_form_for helper, which outputs the inputs in list form.
To keep things as close to the Railscast code as possible, you'll need to:
enclose the collection of nested fields in a wrapper (I use a div with the subjects CSS ID.)
enclose the nested fields in a ul/ol wrapper (I applied a nested-fields CSS class.)
Here's what your files should like.
Teacher form (with nested Subject fields):
- semantic_form_for(#teacher) do |form|
- form.inputs do
= form.input :first_name
= form.input :surname
= form.input :city
%h2 Subjects
#subjects
- form.semantic_fields_for :subjects do |builder|
= render :partial => "subject_fields", :locals => { :f => builder }
.links
= link_to_add_fields "Add Subject", form, :subjects
Subject fields partial (for nested Subject):
%ul.nested-fields
= f.input :name
= f.input :exam
= f.input :level
= link_to_remove_fields "Remove Subject", f
ApplicationHelper:
def link_to_remove_fields(name, f)
f.hidden_field(:_destroy) + link_to_function(name, "remove_fields(this)")
end
def link_to_add_fields(name, f, association)
new_object = f.object.class.reflect_on_association(association).klass.new
fields = f.fields_for(association, new_object, :child_index => "new_#{association}") do |builder|
render(association.to_s.singularize + "_fields", :f => builder)
end
link_to_function(name, h("add_fields(this, \"#{association}\", \"#{escape_javascript(fields)}\")"))
end
Application.js:
function remove_fields(link) {
$(link).previous("input[type=hidden]").value = "1";
$(link).up(".nested-fields").hide();
}
function add_fields(link, association, content) {
var new_id = new Date().getTime();
var regexp = new RegExp("new_" + association, "g")
$(link).up().insert({
before: content.replace(regexp, new_id)
});
}
Using following gems:
formtastic (0.9.10)
haml (3.0.2)
gherkin (1.0.26)
rails (2.3.5)
application.js for jQuery:
function remove_fields(link) {
$(link).prev("input[type=hidden]").val("1");
$(link).parent(".nested-fields").hide();
}
function add_fields(link, association, content) {
var new_id = new Date().getTime();
var regexp = new RegExp("new_" + association, "g")
$(link).parent().before(content.replace(regexp, new_id));
}
In Rails 3, there's no need to use the h() function in the helper. Just omit and it should work.
I've been beating my head against this one on and off for years, and I just today came up with something I'm proud of - elegant, unobtrusive, and you can do it only two or three (really long) lines of code.
ParentFoo has_many NestedBars, with appropriate accepts_nested_parameters and all those goodies. You render a set of fields for a nested_bar into a data-add-nested attribute on the link, using the :child_index to set a string that you can replace later. You pass that string in a second parameter, data-add-nested-replace
parent_foos/_form.html.haml
- semantic_form_for #parent_foo do |f|
-# render existing records
- f.semantic_fields_for :nested_bars do |i|
= render 'nested_bar_fields', :f => i
# magic!
- reuse_fields = f.semantic_fields_for :nested_bars, #parent_foo.nested_bars.build, :child_index => 'new_nested_bar_fields' do |n| render('nested_bar_fields', :f => n) end
= link_to 'Add nested bar', '#', 'data-add-nested' => reuse_fields, 'data-add-nested-replace' => 'new_nested_bar_fields'
the nested fields partial is nothing fancy
parent_foos/_nested_bar_fields.html.haml
- f.inputs do
= f.input :name
= f.input :_delete, :as => :boolean
In your jQuery you bind the click of elements with a data-add-nested field (I've used live() here so ajax-loaded forms will work) to insert the fields into the DOM, replacing the string with a new ID. Here I'm doing the simple thing of inserting the new fields before() the link, but you could also provide an add-nested-replace-target attribute on the link saying where in the DOM you want the new fields to wind up.
application.js
$('a[data-add-nested]').live('click', function(){
var regexp = new RegExp($(this).data('add-nested-replace'), "g")
$($(this).data('add-nested').replace(regexp, (new Date).getTime())).insertBefore($(this))
})
You could put this in a helper, of course; I'm presenting it here directly in the view so the principle is clear without mucking about in metaprogramming. If you're worried about names colliding, you could generate a unique ID in that helper and pass it in nested-replace.
getting fancy with the delete behaviour is left as an exercise for the reader.
(shown for Rails 2 because that's the site I'm working on today - almost the same in Rails 3)

Categories