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:

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:

#73 Complex Forms Part 1 - RailsCasts

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:

... #73 Complex Forms Part 1 - RailsCasts

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).

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

Simone

.