Question regarding entering data in a has_many relationship.

Hey there,

I'm new to Rails and am learning by trying to write a cookbook
application. My question is about the best way to implement adding
multiple steps to a recipe.

Imagine I have a recipe that has a title, author, description and a
number of steps/instructions. Instead of entering all the steps in a
single text box, I'd like to have the user enter each step
individually so that I can later list all steps on one page or skip
through them one per page, for example.

I'm assuming that the way to do this is to have a separate table that
holds the steps for each recipe, similar to the posts/comments
relationship in all the blog examples. However, is there a way of
implementing this such that a user could add all the recipe details
and steps on one page rather than entering the details first and each
step on subsequent table saves?

Does that make any sense? Maybe it's a silly way of trying to
implement a recipe book in the first place? Thoughts?

Thanks.

Ooh_Gravy wrote:

Hey there,

I'm new to Rails and am learning by trying to write a cookbook
application. My question is about the best way to implement adding
multiple steps to a recipe.

Imagine I have a recipe that has a title, author, description and a
number of steps/instructions. Instead of entering all the steps in a
single text box, I'd like to have the user enter each step
individually so that I can later list all steps on one page or skip
through them one per page, for example.

I'm assuming that the way to do this is to have a separate table that
holds the steps for each recipe, similar to the posts/comments
relationship in all the blog examples. However, is there a way of
implementing this such that a user could add all the recipe details
and steps on one page rather than entering the details first and each
step on subsequent table saves?

Does that make any sense? Maybe it's a silly way of trying to
implement a recipe book in the first place? Thoughts?

I don't think it's silly at all. It's how I would approach this myself.
From the controller/model aspect this is pretty straight forward. For
each step you would add them like so: recipe.steps << new_step.

The view is where these things get a little more tricky. There is no
"one right way" to do this. It's going to depend on your specific case.
Often it's a good idea to allow the user to add steps by using AJAX to
insert new form fields to hold all the steps the user may want to enter.

Another possible approach (and one I might consider in this specific
case) would be to allow the user to enter all the instructions (steps)
into a single text area. Then using some basic markup that would allow
you to separate each step and store them individually into the steps
table. It really depends on what ends up to be the best user experience.

I would try to avoid having to refresh the entire page while adding
additional steps. That generally does not provide a great user
experience, but it is a option. It's only real advantage would be that
it would require no JavaScript to work (and so may be okay as a UI
degradation for when JS is not available).

The down-side to the single text area approach is that you would be
relying on the end user to provide the proper delimiters that would
allow you to parse the steps properly.

I think this railscast might help you understand the basics of what you
need:

http://railscasts.com/episodes/73-complex-forms-part-1

This continues in episode 74 as well.

Robert Walker wrote:
[...]

I don't think it's silly at all. It's how I would approach this myself.
From the controller/model aspect this is pretty straight forward. For
each step you would add them like so: recipe.steps << new_step.

The view is where these things get a little more tricky. There is no
"one right way" to do this. It's going to depend on your specific case.
Often it's a good idea to allow the user to add steps by using AJAX to
insert new form fields to hold all the steps the user may want to enter.

Not Ajax, just straight client-side JavaScript. There is no need to
involve the server.

Another possible approach (and one I might consider in this specific
case) would be to allow the user to enter all the instructions (steps)
into a single text area. Then using some basic markup that would allow
you to separate each step and store them individually into the steps
table. It really depends on what ends up to be the best user experience.

This is probably the approach I would take. However, for ingredients,
I'd tend to use the JS approach.

Also, remember that many recipes don't break down easily into discrete
steps.

I would try to avoid having to refresh the entire page while adding
additional steps. That generally does not provide a great user
experience, but it is a option. It's only real advantage would be that
it would require no JavaScript to work (and so may be okay as a UI
degradation for when JS is not available).

The down-side to the single text area approach is that you would be
relying on the end user to provide the proper delimiters that would
allow you to parse the steps properly.

Right.

I think this railscast might help you understand the basics of what you
need:

http://railscasts.com/episodes/73-complex-forms-part-1

This continues in episode 74 as well.

Best,

Marnen Laibow-Koser wrote:

Robert Walker wrote:
[...]

The view is where these things get a little more tricky. There is no
"one right way" to do this. It's going to depend on your specific case.
Often it's a good idea to allow the user to add steps by using AJAX to
insert new form fields to hold all the steps the user may want to enter.

Not Ajax, just straight client-side JavaScript. There is no need to
involve the server.

That was boneheaded of me! All that's needed is client-side DOM
manipulation with no server involvement. The AJAX acronym should just go
way. It's used to generally and really has no clear meaning anyway. And
here I am perpetuation the problem. :slight_smile:

Robert Walker wrote:

Marnen Laibow-Koser wrote:

Robert Walker wrote:
[...]

The view is where these things get a little more tricky. There is no
"one right way" to do this. It's going to depend on your specific case.
Often it's a good idea to allow the user to add steps by using AJAX to
insert new form fields to hold all the steps the user may want to enter.

Not Ajax, just straight client-side JavaScript. There is no need to
involve the server.

That was boneheaded of me! All that's needed is client-side DOM
manipulation with no server involvement. The AJAX acronym should just go
way. It's used to generally and really has no clear meaning anyway. And
here I am perpetuation the problem. :slight_smile:

Ajax (specifically not an acronym according to its creator, although it
really is one) has a very clear meaning -- asynchronous JavaScript
server requests that don't require a page reload. The term is useful
and I see no reason to drop it -- if it's used in its proper sense.
This wasn't it. :slight_smile:

Best,

Robert Walker wrote:

...
http://railscasts.com/episodes/73-complex-forms-part-1

This continues in episode 74 as well.

With the newer accepts_nested_attributes_for method he could probably
save some work/code (http://is.gd/5FLVB).

So I've been reading just that blog entry but can't get it to work.
I've create a "recipes" table with name, author and a description.
I've then added a table called "steps" and updated my
recipes_controller with the @recipe.steps.build line that seems to be
needed. However, I get an ActionController error saying that create
has been passed an unknown method step.

I've obvioulsy missed out a piece of code somewhere but can't spot it
from reading any of the tutorials.

Marnen Laibow-Koser wrote:

Ajax (specifically not an acronym according to its creator, although it
really is one) has a very clear meaning -- asynchronous JavaScript
server requests that don't require a page reload. The term is useful
and I see no reason to drop it -- if it's used in its proper sense.
This wasn't it. :slight_smile:

Asynchronous JavaScript And XML (AJAX). So if you don't send XML in the
asynchronous request is it still AJAX? I still find this a confusing
term for nothing more than background remote request. Wether is has a
clear meaning or not, it still gets misused a lot rendering the term
near worthless.

Robert Walker wrote:

Marnen Laibow-Koser wrote:

Ajax (specifically not an acronym according to its creator, although it
really is one) has a very clear meaning -- asynchronous JavaScript
server requests that don't require a page reload. The term is useful
and I see no reason to drop it -- if it's used in its proper sense.
This wasn't it. :slight_smile:

Asynchronous JavaScript And XML (AJAX). So if you don't send XML in the
asynchronous request is it still AJAX?

Yes. The XML part is usually a misnomer, but the rest of the term is
quite apt.

I still find this a confusing
term for nothing more than background remote request.

"Nothing more than"? This was a fundamental change to the structure of
Web apps.

Wether is has a
clear meaning or not, it still gets misused a lot rendering the term
near worthless.

A lot of terms get misused. That doesn't make them worthless when used
properly.

If you want worthless terms, go pick on "Web 2.0"... :slight_smile:

Best,

OK, so my previous errors seemed to be due to a Windows environment
issue. Now I'm back on my Mac things are all working.
Anyway....thanks for the help and pointing in the direction of those
screencasts. I found Ryan's updated nested form example on Github
(complex-form-examples) and it's just what I was looking for.
However, I do have a further question regarding table id's.

In my application I have a recipe with multiple steps. Using the
complex form example, I can create, edit and delete the steps of a
recipe. However, the step id's are always incrementing. I.E. I
create two steps which have id's of 1 and 2 respectively. If I now
delete the second step and add another I now have id's 1 and 3. id 2
has been 'consumed' which means that a simple loop using the id's to
generate an html list won't work.

Is this just the way table id's work when they are deleted or is it
specific to this implementation in the complex form example? Is the
solution to work out how many steps are present and increment an
integer to use as the index in the template?

Thanks.

OK, thanks. What I wanted to do was this:

  <ul>
    <% @recipe.steps.each do |step| %>
      <li><%= step.id %>: <%= step.step %></li>
    <% end %>
  </ul>

but that would now result in:

* 1: This is the first step
* 3: This is the second step

So I now need to use a helper to generate a step index I can use to
display to the user.

So answering my own question, is this ok or is there a cleaner way to
do it?

  <ul>
    <% index = 1 %>
    <% @recipe.steps.each do |step| %>
      <li><%= index %>: <%= step.step %></li>
      <% index += 1 %>
    <% end %>
  </ul>

Thanks a lot.

You would want each_with_index.

    <% @recipie.steps.each do |step, index| %>
  • <%= index %>: <%= step.step %>

<% end %>

A little cleaner, and handles updating the index for you.

-Steve

Ooh_Gravy wrote:

So answering my own question, is this ok or is there a cleaner way to
do it?

  <ul>
    <% index = 1 %>
    <% @recipe.steps.each do |step| %>
      <li><%= index %>: <%= step.step %></li>
      <% index += 1 %>
    <% end %>
  </ul>

Thanks a lot.

You can use the OL (ordered list ) tag instead of UL tag (unordered
list).

http://www.w3schools.com/TAGS/tag_ol.asp

  <ol>
    <% @recipe.steps.each do |step| %>
      <li>: <%= step.step %></li>
    <% end %>
  </ol>

Simone

.