Adding form fields (extending a form) on the fly


#1

Hello,

I just can’t seem to find a way to extend a form dynamically in ROR.

Say I am writing a recipe website. There is one form to enter the
recipe. There is room for N number of ingredients (let’s say a text
field for each ingredient name and selection list for the amount). What
if the user wants to add more than N ingredients as he types them in?
how do I do this without a POST, and have those new fields included in
the request (i guess they need to have a unique name and id).

thank you.
Alon.


#2

You could have a link on the page called ‘Add extra ingredient field’
that uses ajax to add in more fields as necessary. Put the ingredient
field in a partial (_ingredient.rhtml) so you don’t have to repeat
yourself.

View:

<% 5.times do %> <%= render :partial => 'ingredient' %> <% end %>
<%= link_to_remote 'Add extra ingredient', :update => 'ingredients', :position => :bottom, :url => { :action => :extra_ingredient } %>

Controller:

def extra_ingredient
render :partial => ‘ingredient’
end

I haven’t tested this, but something like this should work fine.

Cheers, Jonny.

goldshuv wrote:

Hello,

I just can’t seem to find a way to extend a form dynamically in ROR.

Say I am writing a recipe website. There is one form to enter the
recipe. There is room for N number of ingredients (let’s say a text
field for each ingredient name and selection list for the amount). What
if the user wants to add more than N ingredients as he types them in?
how do I do this without a POST, and have those new fields included in
the request (i guess they need to have a unique name and id).

thank you.
Alon.


#3

Jonny,

Thanks a lot. I appreciate it. I did try something like this earlier but
I am running into a problem. The form may have all the extra fields the
user requested, but they all have the same name and id:

for example, if my text_field looks something like this

<%= text_field(:ingredient, :name) %>

rendering it more times will render the same exact thing over and over

<%= text_field(:ingredient, :name) %>
<%= text_field(:ingredient, :name) %>
<%= text_field(:ingredient, :name) %>

and when the form is submitted the request will include only one entry
for ingredient => { name => “sometext”}. and all the text that the user
entered (besides the first one) will not be there.

thanks,
Alon.

Jonathan V. wrote:

You could have a link on the page called ‘Add extra ingredient field’
that uses ajax to add in more fields as necessary. Put the ingredient
field in a partial (_ingredient.rhtml) so you don’t have to repeat
yourself.

View:

<% 5.times do %> <%= render :partial => 'ingredient' %> <% end %>
<%= link_to_remote 'Add extra ingredient', :update => 'ingredients', :position => :bottom, :url => { :action => :extra_ingredient } %>

Controller:

def extra_ingredient
render :partial => ‘ingredient’
end

I haven’t tested this, but something like this should work fine.

Cheers, Jonny.

goldshuv wrote:

Hello,

I just can’t seem to find a way to extend a form dynamically in ROR.

Say I am writing a recipe website. There is one form to enter the
recipe. There is room for N number of ingredients (let’s say a text
field for each ingredient name and selection list for the amount). What
if the user wants to add more than N ingredients as he types them in?
how do I do this without a POST, and have those new fields included in
the request (i guess they need to have a unique name and id).

thank you.
Alon.


#4

I do believe you can also specify the recipes to be an array. Try

<%= text_field(“ingredient”, :name[] %>

Or something to that effect. I’m 99% sure there is a way to do this,
but I
don’t have time to investigate or test atm.

-B


#5

On Mon, Jan 23, 2006 at 01:48:45PM +0100, Jonathan V. wrote:
} You could have a link on the page called ‘Add extra ingredient field’
} that uses ajax to add in more fields as necessary. Put the ingredient
} field in a partial (_ingredient.rhtml) so you don’t have to repeat
} yourself.
[…]

There is no need for AJAX. Take a look at
http://www.anthropohedron.net/bookmarkfeeds/ for an example that just
uses
JavaScript to create a new line and I handle it with Ruby pretty easily.
The source is freely available (it was a simple toy I used to learn Ruby
and RoR, so consider it public domain) at
http://www.anthropohedron.net/bookmarkfeeds/src.tgz

} I haven’t tested this, but something like this should work fine.
} Cheers, Jonny.
–Greg

} goldshuv wrote:
} > Hello,
} >
} > I just can’t seem to find a way to extend a form dynamically in ROR.
} >
} > Say I am writing a recipe website. There is one form to enter the
} > recipe. There is room for N number of ingredients (let’s say a text
} > field for each ingredient name and selection list for the amount).
What
} > if the user wants to add more than N ingredients as he types them
in?
} > how do I do this without a POST, and have those new fields included
in
} > the request (i guess they need to have a unique name and id).
} >
} > thank you.
} > Alon.
}
} –
} Posted via http://www.ruby-forum.com/.
} _______________________________________________
} Rails mailing list
} removed_email_address@domain.invalid
} http://lists.rubyonrails.org/mailman/listinfo/rails
}


#6

Thanks guys,

Well still no luck but I appreciate all the suggestions.

first suggestion (timestamping) gives this:
`@ingredient[Mon Jan 23 18:57:50 IST 2006]’ is not allowed as an
instance variable name

second suggestion (arrays) gives this:
undefined method `[]’ for :quantity_whole:Symbol

and I can’t seem to find this use case (cloning the same form field) in
the bookmark feeds site (but thanks for the source).

still searching…

thanks,
Alon.


#7

I’m about to do something similar. Have a look on here:
http://wiki.rubyonrails.org/rails/pages/UnderstandingRequestParameters

You need to do something like
<% index = Time.now.to_s -%>
<%= text_field(“ingredient[#{index}]”, :name) %>

to generate a temporary key for each ingredient. But in the action that
actually creates/saves all the ingredients, don’t use this key as the
id, instead let the database autonumber them for you (not 100% sure on
details of this bit as I haven’t done it yet!)

Michael

goldshuv wrote:

Jonny,

Thanks a lot. I appreciate it. I did try something like this earlier but
I am running into a problem. The form may have all the extra fields the
user requested, but they all have the same name and id:

for example, if my text_field looks something like this

<%= text_field(:ingredient, :name) %>

rendering it more times will render the same exact thing over and over

<%= text_field(:ingredient, :name) %>
<%= text_field(:ingredient, :name) %>
<%= text_field(:ingredient, :name) %>

and when the form is submitted the request will include only one entry
for ingredient => { name => “sometext”}. and all the text that the user
entered (besides the first one) will not be there.

thanks,
Alon.

Jonathan V. wrote:

You could have a link on the page called ‘Add extra ingredient field’
that uses ajax to add in more fields as necessary. Put the ingredient
field in a partial (_ingredient.rhtml) so you don’t have to repeat
yourself.

View:

<% 5.times do %> <%= render :partial => 'ingredient' %> <% end %>
<%= link_to_remote 'Add extra ingredient', :update => 'ingredients', :position => :bottom, :url => { :action => :extra_ingredient } %>

Controller:

def extra_ingredient
render :partial => ‘ingredient’
end

I haven’t tested this, but something like this should work fine.

Cheers, Jonny.

goldshuv wrote:

Hello,

I just can’t seem to find a way to extend a form dynamically in ROR.

Say I am writing a recipe website. There is one form to enter the
recipe. There is room for N number of ingredients (let’s say a text
field for each ingredient name and selection list for the amount). What
if the user wants to add more than N ingredients as he types them in?
how do I do this without a POST, and have those new fields included in
the request (i guess they need to have a unique name and id).

thank you.
Alon.


#8

Hello Everyone,

I hope you AJAX pros don’t mind me asking such a simple question, but
it’s been baffling me all weekend. I’m trying to learn the AJAX side
of rails and struggling with what I though would be fairly simple
operations. I’d like to add rows to a table, and use an effect, like
Slide Down or Highlight. However, when I tried to add them in the way
recommended by the book I’m going off of, it didn’t work. Here’s some
snippits of what I have:

My view:

<% content_for(“page_scripts”) do -%>
function item_added()
{
var item = $(‘mytable’).lastChild.lastChild;
new Effect.Highlight(item);
}
<% end -%>

Listing

<%= link_to ‘New’, :action => ‘new’ %>

<%= render(:partial => 'mytable') -%>


_mytable.rhtml:

Col1 Col2 Col3 ... Col9 <%= render(:partial => 'row', :collection => @items) -%> ------------------------------- _row.rhtml: ------------------------------- <%=h item.val1 %> <%=h item.val2 %> <%=h item.val3 %> ... <%=h item.val9 %> ------------------------------- Controller's new action: ------------------------------- def new item = MyClass.new() render(:partial => 'row', :object => item, :layout => false) end

The row is added by AJAX but no effect is called… any idea what I’m
doing wrong?

-Josh


#9

Well, i hope so too, but it’s pretty clear from the request dump that
only the first one is saved…

Alon.


#10

Hmm, from the Agile book, pg. 355:

“When the user submits the form, the raw POST data is sent back to our
application. Rails
extracts the fields from the form and constructs the params hash.
Simple values (such as
the id field, extracted by routing the form action) are stored as
scalars in the hash.
But, if a parameter name has brackets in it, Rails assumes that is it
part of more
structured data and constructs a hash to hold the key values.
Inside
this hash, the
string inside the brackets is used as the key.”

So, Rails adds it’s own data structure communication nomenclature on top
of basic HTTP.
That’s groovy and all, but I personally find it extremely disconcerting
that I have to
bend my html to fit the wishes of rails. And what if – as was I think
the case of the OP
– I don’t have anything to use as a key… if I’m simply inputting a
list of values?

I don’t know if any rails devs are listening here, but I have to say
that I’m fairly
stunned at this oversight. The code handling request params should
simply default to
creating an array for each name found in the headers and wrap this in an
accessor that
returns the first value for single params. It’s fine if you want to add
bells and whistles
and allow people to create hashes by putting square brackets in the name
attribute, but it
is a tremendous dent to rails’ credibility in my book if it can’t handle
multiple params
with the same name.

b


#11

goldshuv wrote:

<%= text_field(:ingredient, :name) %>
<%= text_field(:ingredient, :name) %>

and when the form is submitted the request will include only one entry
for ingredient => { name => “sometext”}. and all the text that the user
entered (besides the first one) will not be there.

It is perfectly valid to send multiple values for the same http post
param. Rails should
make that available as an array…

Hmmm, I just went and checked and it appears that Rails only keeps the
first value.
Someone please tell me that I’m just not looking at my @params right…
tell me that rails
is NOT throwing away the other perfectly valid input params! Post params
with the same
name should simply be put into an array.

Hoping someone can set me straight on this!

b


#12

On Jan 23, 2006, at 10:46 AM, Ben M. wrote:

name found in the headers and wrap this in an accessor that returns
the first value for single params. It’s fine if you want to add
bells and whistles and allow people to create hashes by putting
square brackets in the name attribute, but it is a tremendous dent
to rails’ credibility in my book if it can’t handle multiple params
with the same name.

b

Ben-

Of course you can accomplish what you want with rails params system.

Once you understand how it works I’m sure you will see how nice it is
to work with. If you want a list of text_fields to show up in an
array in the params all you need to do is something like this:

Then the an array of list items will be available in your controller

with params[:list] . Its’ really very simple. By using the empty []
array constructor after the name of the field, rails will
automatically recognize this and create an array in the params hash
with a key of :list. So if you entered the following entries into
your six text fields:

field1
field2
field3
field4
field5
field6

Then the following would be true:

params[:list] == [‘field1’, ‘field2’, ‘field3’, ‘field4’, ‘field5’,
‘field6’]

Hope that clears it up for you.

Cheers-
-Ezra Z.
Yakima Herald-Republic
WebMaster
http://yakimaherald.com
509-577-7732
removed_email_address@domain.invalid


#13

You would be better off doing the effect with either RJS, or as a
callback from the ajax request. To do it the second way, add something
like this to the ajax

:complete => visual_effect(:highlight, <<element_id>>)

Or, to do it your way, you should be calling Effect.Highlight with an
id, not an actual element, ie new Effect.Highlight(item.id);. You should
also check that item is actually the correct element with a valid id.
Just use alert(item.id); to quickly check that.

And… to use ajax, your link_to tag should should be link_to_remote:

<%= link_to_remote :update => ‘mytable’, :position => :bottom , :url =>
{ :action => :new } %>

Cheers, Jonny.

Joshua Gitlin wrote:

Hello Everyone,

I hope you AJAX pros don’t mind me asking such a simple question, but
it’s been baffling me all weekend. I’m trying to learn the AJAX side
of rails and struggling with what I though would be fairly simple
operations. I’d like to add rows to a table, and use an effect, like
Slide Down or Highlight. However, when I tried to add them in the way
recommended by the book I’m going off of, it didn’t work. Here’s some
snippits of what I have:

My view:

<% content_for(“page_scripts”) do -%>
function item_added()
{
var item = $(‘mytable’).lastChild.lastChild;
new Effect.Highlight(item);
}
<% end -%>

Listing

<%= link_to ‘New’, :action => ‘new’ %>

<%= render(:partial => 'mytable') -%>


_mytable.rhtml:

Col1 Col2 Col3 ... Col9 <%= render(:partial => 'row', :collection => @items) -%> ------------------------------- _row.rhtml: ------------------------------- <%=h item.val1 %> <%=h item.val2 %> <%=h item.val3 %> ... <%=h item.val9 %> ------------------------------- Controller's new action: ------------------------------- def new item = MyClass.new() render(:partial => 'row', :object => item, :layout => false) end

The row is added by AJAX but no effect is called… any idea what I’m
doing wrong?

-Josh


#14

You should be able to repeat <%= text_field ‘ingredient[]’, ‘name’ %> as
many times as you want, and be able access all of them in the params
hash.

Second suggestion:

You shouldn’t use :quantity_whole[], I don’t think symbols will work
here. Do ‘quantity_whole[]’ instead.

Gregory:
You are correct, you don’t have to use ajax, but if you don’t you have
to repeat the code used to create the extra field in javascript. Ajax
helps with DRY in this case.

-Jonny.

goldshuv wrote:

Thanks guys,

Well still no luck but I appreciate all the suggestions.

first suggestion (timestamping) gives this:
`@ingredient[Mon Jan 23 18:57:50 IST 2006]’ is not allowed as an
instance variable name

second suggestion (arrays) gives this:
undefined method `[]’ for :quantity_whole:Symbol

and I can’t seem to find this use case (cloning the same form field) in
the bookmark feeds site (but thanks for the source).

still searching…

thanks,
Alon.


#15

On Mon, Jan 23, 2006 at 09:24:19PM +0100, Jonathan V. wrote:
[…]
} Gregory:
} You are correct, you don’t have to use ajax, but if you don’t you
have
} to repeat the code used to create the extra field in javascript. Ajax
} helps with DRY in this case.

DRY is all well and good, but it can be taken too far. If I can safely
do
something solely on the client side to avoid a server roundtrip (and
make
no mistake, AJAX is still a server roundtrip), I will do it that way
almost
every time. (Note that I said “safely.” There are times when it is
inappropriate to execute code on the client side, particularly when it
involves moving too much or sensitive data to the client side and
possibly
back or when doing so could allow bad data to be accepted by the
server.)

Furthermore, the JavaScript solution I gave degrades nicely on browsers
with JavaScript turned off or entirely unavailable. When the page is
loaded
there is always a blank row for the user to add another line. If
JavaScript
is available, a button is created that will dynamically add another
blank
row. Any rows left blank are ignored by the controller code, but if that
original blank row is filled then the controller will accept it as
another
row. The user with JavaScript can add several rows between server
roundtrips, but the user without JavaScript can still add one row per
server roundtrip. Once you start relying on AJAX, you start making it
harder to degrade nicely.

} -Jonny.
–Greg


#16

Jonathan V. wrote:

You should be able to repeat <%= text_field ‘ingredient[]’, ‘name’ %> as
many times as you want, and be able access all of them in the params
hash.

Has anyone who’s suggested this actually tried it and got it to work?
I’ve tried repeating a form element with name like ‘ingredient[]’ and
setting the form to an action/view which simply calls debug(params). I
only get a single entry in the “ingredient” hash, with an empty key,
like this:

ingredient: !ruby/hash:HashWithIndifferentAccess
‘’: !ruby/hash:HashWithIndifferentAccess
name: ‘’
amount: ‘’


#17

Ezra Z. wrote:

Its’ really very simple. By using the
empty [] array constructor after the name of the field, rails will
automatically recognize this and create an array in the params hash
with a key of :list.

Thanks Ezra. I see that that does indeed work. For the record, I think
it is quite silly
of rails to require empty square brackets just to know that it’s got an
array. Seems like
someone was thinking PHP when they decided that. :wink:

Speaking of which, I think this wiki page is out of date:

http://wiki.rubyonrails.org/rails/pages/UnderstandingRequestParameters#fn1

"That this works seems only logical, because in PHP array[] syntax is
used to push new
elements to an array.

However, this is not possible in Rails, so youâ??ll have to improvize by
inputting arrays
with explicit indices. Although it must be admitted that the PHP
notation is very
convenient, you can do the same thing and more with explicit indices in
Rails."

b