在嵌套表单中使用jQuery Tokeninput部分

时间:2014-12-01 02:46:16

标签: ruby-on-rails partials jquery-tokeninput cocoon-gem

我正在使用jQuery Tokeninput,如Railscast所示。我想以嵌套的形式组合这个功能,但得到错误

undefined method `artists' for #<SimpleForm::FormBuilder:0x007febe0883988>

由于某种原因,它无法识别我的表单构建器中的track参数,这阻止了我记录下的相册。

<div class="input">
  <%= f.input :artist_tokens, label: 'Featured Artists', input_html: {"data-pre" => f.artists.map(&:attributes).to_json} %>
</div>

请注意,这可以在我的track表单中使用,但从嵌套后的album表单中找不到。我该怎么做才能让它发挥作用?

class ArtistsController < ApplicationController
    def index
      @artists = Artist.order(:name)
      respond_to do |format|
          format.html
          format.json {render json: @artists.tokens(params[:q])}
      end
    end
end

模型

class Artist < ActiveRecord::Base
    has_many :album_ownerships
    has_many :albums, through: :album_ownerships

    has_many :featured_artists
    has_many :tracks, through: :featured_artists


    def self.tokens(query)
      artists = where("name like ?", "%#{query}%")

      if artists.empty?
        [{id: "<<<#{query}>>>", name: "Add New Artist: \"#{query}\""}]
      else
        artists
      end
    end

    def self.ids_from_tokens(tokens)
      tokens.gsub!(/<<<(.+?)>>>/) {create!(name: $1).id}
      tokens.split(',')
    end
end

class Albums < ActiveRecord::Base
    attr_reader :artist_tokens

    accepts_nested_attributes_for :tracks, :reject_if => :all_blank, :allow_destroy => true

    has_many :albums_ownerships
    has_many :artists, through: :albums_ownerships

    def artist_tokens=(ids)
        self.artist_ids = Artist.ids_from_tokens(ids)
    end
end

class Track < ActiveRecord::Base
    attr_reader :artist_tokens

    belongs_to :album

    has_many :featured_artists
    has_many :artists, through: :featured_artists

    def artist_tokens=(ids)
        self.artist_ids = Artist.ids_from_tokens(ids)
    end
end


class AlbumOwnership < ActiveRecord::Base
    belongs_to :artist
    belongs_to :album
end

class FeaturedArtist < ActiveRecord::Base
    belongs_to :artist
    belongs_to :track
end

相册表格

<%= simple_form_for(@album) do |f| %>
  <div class="field">
    <%= f.label :name %><br>
    <%= f.text_field :name %>
  </div>

  <h1>Tracks</h1>

  <%= f.simple_fields_for :tracks do |track| %>
    <%= render 'track_fields', :f => track %>
  <% end %>
  <div id='links'>
    <%= link_to_add_association 'Add Field', f, :tracks %>
  </div>

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

跟踪部分

<div class="field">
  <%= f.input :name %><br>
</div>

<div class="input">
  <%= f.input :artist_tokens, label: 'Featured Artists', input_html: {"data-pre" => f.artists.map(&:attributes).to_json} %>
</div>

JS

$(function() {
    $('#track_artist_tokens').tokenInput('/artists.json', {
        prePopulate: $("#track_artist_tokens").data("pre"),
        theme: 'facebook',
        resultsLimit: 5
    });
});

更新

如nathanvda所述,我需要使用f.object才能识别艺术家。所以在我的部分我现在有:

<%= f.input :artist_tokens, label: 'Featured Artists', input_html: {"data-pre" => f.object.artists.map(&:attributes).to_json, class: 'test_class'} %>

在我的js中,我还需要在插入之前/之后调用令牌输入法:

$(function() {  
    $('.test_class').tokenInput('/artists.json', {
        prePopulate: $(".test_class").data("pre"),
        theme: 'facebook',
        resultsLimit: 5
    });


    $('form').bind('cocoon:after-insert', function(e, inserted_item) {
        inserted_item.find('.test_class').tokenInput('/artists.json', {
            prePopulate: $(".test_class").data("pre"),
            theme: 'facebook',
            resultsLimit: 5
        });
    });
});

我唯一剩下的问题是tracks_attributes没有被保存。我在过去post遇到了与此类似的问题,但两个主要区别是涉及的第二级嵌套,我在嵌套表单中使用了连接表。我不完全确定这些代码是否或如何转换,但我相信这很可能是问题所在。至于我albums_controller允许的参数,这就是它们的样子。

def album_params
  params.require(:album).permit(:name, :artist_tokens, tracks_attributes: [:id, :name, :_destroy, :track_id])
end

2 个答案:

答案 0 :(得分:1)

如果您需要访问表单的对象,则需要编写f.object,因此我认为您应该只写f.object.artists

答案 1 :(得分:0)

您的"data-pre" => f.artists...正在artists上调用f方法,该方法是表单构建器,并且没有#artists方法。

请改为尝试:

在相册表单中,将渲染部分行更改为:

<%= render 'track_fields', :f => track, :artists => @artists %>

然后在轨道部分中使用它:

<%= f.input :artist_tokens, label: 'Featured Artists', input_html: {"data-pre" => artists.map(&:attributes).to_json} %>

已更新

让我们退一步吧。在您的代码中,您似乎需要使用data-pre集合的属性填充artists属性。

问题是您正在调用f.artists,其中f是FormBuilder,并且对artists一无所知。这就是你得到undefined method 'artists'...

的原因

解决方案是为视图及其部分提供一系列艺术家。一种方法:

class AlbumsController < ApplicationController
  ...
  def new
    @album = Album.new
    @artists = Artist.order(:name) # or some other subset of artists
  end

  ...

  def edit
    @album = Album.find params[:id]
    @artists = Artist.order(:name) # or perhaps "@artists = @album.artists", or some other subset of artists
  end
end

然后在new.html.erbedit.html.erb中,将@artists传递给表单partial:

... # other view code
  <%= render 'form', album: @album %>
... # other view code

然后以你的形式部分:

... # other view code
<%= f.simple_fields_for :tracks do |track_form| %>
  <%= render 'track_fields', :f => track_form %>
<% end %>
... # other view code

最后,在你的音轨部分:

... # other view code
<div class="input">
  <%= f.input :artist_tokens, label: 'Featured Artists', input_html: {"data-pre" => @artists.map(&:attributes).to_json} %>
</div>
... # other view code

这有意义吗?