3

I'm following Railcasts 197 in order to make a nested form to my application. What I'm trying to do is displaying a "remove" link which hides a row from the form using jQuery.

enter image description here

If I click in remove, nothing happens, but if I hit Submit and there are errors on page, the row is removed!

enter image description here

I've been trying to fix for days but I just can't see if something is missing or I need to add something, any help would be appreciated.

Code:

model/album_review_proposal.rb

class AlbumReviewProposal < ActiveRecord::Base
  attr_accessible :description, :user_id, :album_attributes
  validates :description, :presence => true

  has_one :album, :dependent => :destroy
  accepts_nested_attributes_for :album , :allow_destroy => true
end

model/album.rb

class Album < ActiveRecord::Base
  attr_accessible :album_name, :artist,  :year, :tracks_attributes

  validates :album_name, :artist, :presence => true
  validates :year, :numericality => { :only_integer => true }

  belongs_to :album_review_proposal
  has_many :tracks
  accepts_nested_attributes_for :tracks, :allow_destroy => true
end

model/track.rb

class Track < ActiveRecord::Base
  attr_accessible :track_name, :track_number

  validates :track_name, :track_number,  :presence => true

  belongs_to :album
end

views/album_review_proposals/_form.html.erb

<%= form_for(@album_review_proposal) do |f| %>
  <% if @album_review_proposal.errors.any? %>
    <div id="error_explanation">
      <h2><%= pluralize(@album_review_proposal.errors.count, "error") %> prohibited this album_review_proposal from being saved:</h2>

      <ul>
      <% @album_review_proposal.errors.full_messages.each do |msg| %>
        <li><%= msg %></li>
      <% end %>
      </ul>
    </div>
  <% end %>

    <%= f.fields_for :album do |album| %>
        <div class="field">
          <%= album.label :album_name %><br />
          <%= album.text_field :album_name %>
        </div>
        <div class="field">
          <%= album.label :artist %><br />
          <%= album.text_field :artist %>
        </div>
        <div class="field">
          <%= album.label :year %><br />
          <%= album.text_field :year %>
        </div>

        <div class="field">
          <%= f.label :description %><br />
          <%= f.text_area :description %>
        </div>

        <legend>Tracks</legend>
        <table>
            <th>#</th>
            <th>Name</th>
            <th></th>

            <%= album.fields_for :tracks do |track|  %>
            <tr>
            <div  class="fields">
                <td><%= track.text_field :track_number, :size => 2 %></td>
                <td><%= track.text_field :track_name, :size => 50 %> </td>

              <td><%= track.hidden_field :_destroy %></td>
                <td><%= link_to_remove_fields "remove", track   %></td>
            </div>
            </tr>
            <% end %>
        </table>

    <% end %>

  <div class="actions">
    <%= f.submit %>
  </div>
<% end %>

controllers/album_review_proposals.controller.rb (Only modified method from scaffold)

  def new
    @album_review_proposal = AlbumReviewProposal.new

    @album = @album_review_proposal.build_album

    4.times { @album.tracks.build }

    respond_to do |format|
      format.html # new.html.erb
      format.json { render json: @album_review_proposal  }
    end
  end

application.js

//= require jquery
//= require jquery_ujs
//= require_tree .

function remove_fields(link) {
    $(link).prev("input[type=hidden]").val("1");
    $(link).closest(".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));
}

application_helper.rb

module 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, "add_fields(this, \"#{association}\", \"#{escape_javascript(fields)}\")")
  end


end

HTML part of the HTML I want to remove

 <legend>Tracks</legend>
        <table>
          <thead>
          <tr>
            <th>#</th>
            <th>Name</th>
            <th></th>
          </tr>
          </thead>
          <tbody>

            <div class="fields">
              <td><input id="album_review_proposal_album_attributes_tracks_attributes_0_track_number" name="album_review_proposal[album_attributes][tracks_attributes][0][track_number]" size="2" type="text" /></td>
              <td><input id="album_review_proposal_album_attributes_tracks_attributes_0_track_name" name="album_review_proposal[album_attributes][tracks_attributes][0][track_name]" size="50" type="text" /></td>
              <td><input id="album_review_proposal_album_attributes_tracks_attributes_0__destroy" name="album_review_proposal[album_attributes][tracks_attributes][0][_destroy]" type="hidden" value="false" /><a href="#" onclick="remove_fields(this); return false;">remove</a></td>
            </tr>

            <div class="fields">
              <td><input id="album_review_proposal_album_attributes_tracks_attributes_1_track_number" name="album_review_proposal[album_attributes][tracks_attributes][1][track_number]" size="2" type="text" /></td>
              <td><input id="album_review_proposal_album_attributes_tracks_attributes_1_track_name" name="album_review_proposal[album_attributes][tracks_attributes][1][track_name]" size="50" type="text" /></td>
              <td><input id="album_review_proposal_album_attributes_tracks_attributes_1__destroy" name="album_review_proposal[album_attributes][tracks_attributes][1][_destroy]" type="hidden" value="false" /><a href="#" onclick="remove_fields(this); return false;">remove</a></td>
            </tr>

            <div class="fields">
              <td><input id="album_review_proposal_album_attributes_tracks_attributes_2_track_number" name="album_review_proposal[album_attributes][tracks_attributes][2][track_number]" size="2" type="text" /></td>
              <td><input id="album_review_proposal_album_attributes_tracks_attributes_2_track_name" name="album_review_proposal[album_attributes][tracks_attributes][2][track_name]" size="50" type="text" /></td>
              <td><input id="album_review_proposal_album_attributes_tracks_attributes_2__destroy" name="album_review_proposal[album_attributes][tracks_attributes][2][_destroy]" type="hidden" value="false" /><a href="#" onclick="remove_fields(this); return false;">remove</a></td>
            </tr>

            <!-- repeat 3 times... -->
            </tr>
          </tbody>
        </table>
5
  • Do you have a particular reason for doing it yourself? You could use cocoon - github.com/nathanvda/cocoon Commented Jan 22, 2013 at 19:26
  • Good job in preparing this question. It looks like the issue must be related to your remove_fields javascript function. Can you please post the actual HTML markup that is output around the fields you want to be able to remove? Commented Jan 22, 2013 at 19:28
  • @Edward Actually i'm new on rails so It's good to learn. Commented Jan 22, 2013 at 19:32
  • @Andrew The question is already answered, but I posted the HTML part. Commented Jan 22, 2013 at 19:35
  • Yeah, I was thinking along the same line as @MichelHenrich, but I wanted to see your markup output to be sure there wasn't anything else wrong with the selectors. Glad your question got answered! Commented Jan 22, 2013 at 22:00

1 Answer 1

2

In your view, at views/album_review_proposals/_form.html.erb, instead of adding the class fields to a div inside each tr of a track, add it directly to the tr. The problem is that the div doesn't behave as expected when added inside a table row.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.