Ceará On Rails 2009 - Vou Participar Art Of Community

Comentários: (5)

Nested Attributes usando paperclip

Categoria : Rails, Ruby

Nested Attributes é um ponto interessante que veio junto com o Rails 2.3. Chegou para simplificar o uso de formulários complexos e simplificar o acesso desses atributos.

Este artigo será um tutorial de como usar nested attributes em conjunto com models que trabalham com imagens e fazendo upload usando o paperclip.

Para começarmos, ja vamos partir do pré-suposto que você já possua instalado o rails e todas as gems que fazem parte e que já tenha instalado o paperclip.

Para começar vamos criar um novo projeto Rails para este tutorial:

picture-1

Agora vamos gerar dois scaffolds, um para Product e outro para Picture, model que será responsável por tratar as imagens:

picture-2

picture-3

Depois de tudo gerado, é hora de brincarmos com os models. Primeiro vamos dizer que o model Product possue várias Pictures e adicionarmos que ele aceita os atributos aninhados para Pictures:

    1 class Product < ActiveRecord::Base
    2   has_many :picture
    3   accepts_nested_attributes_for :pictures, :allow_destroy => true
    4 end
    5

Agora, vamos dizer que picture possue um product e que possue um arquivo em anexo chamado image:

    1 class Picture < ActiveRecord::Base
    2   belongs_to :product
    3   has_attached_file :image, :styles => { :thumb => "100x100>" }
    4 end
    5

Com isso, os models já funcionam com atributos aninhados. As views agora! Vamos adicionar a Picture em new.html.erb e adicionar uma partial, essa partial é o inicio das mágicas ;)

    1 <h1>New product</h1>
    2
    3 <% form_for(@product, :html => { :multipart => true }) do |f| %>
    4   <%= f.error_messages %>
    5
    6   <p>
    7     <%= f.label :name %><br />
    8     <%= f.text_field :name %>
    9   </p>
   10
   11   <div id="pictures">
   12     <p>
   13       <% f.fields_for :pictures do |pictures_form| %>
   14           <%= render :partial => 'picture', :locals => { :f => pictures_form } %>
   15       <% end %>
   16     </p>
   17   </div>
   18
   19   <p>
   20     <%= f.submit 'Create' %>
   21   </p>
   22 <% end %>
   23
   24 <%= link_to 'Back', products_path %>

A partial vai ficar assim:

<div>
  <p>
    <% if f.object.new_record? %>
      <%= f.label :image, "Imagem" %> <br /> <%= f.file_field :image %>
    <% else %>
      <%= f.label :image, "Imagem" %> <br /> <%= image_tag f.object.image.url(:thumb) %>
      <%= f.check_box :_delete %>
      <%= f.label :_delete, 'Excluir?' %>
    <% end %>
  </p>
</div>

Se o formulário passado pra view for um objeto novo, então ele vai mostrar um file field para uma foto, se não for, ele vai carregar um thumb e adicionar uma box para deletar a imagem.

Agora, vamos adicionar um método no helper, que vai adicionar dinamicamente novos arquivos para fazer upload:

    1 module ProductsHelper
    2   def add_picture(form)
    3     link_to_function 'Add Picture' do |page|
    4       form.fields_for :pictures, Picture.new, :child_index => 'NEW_RECORD' do |f|
    5         html = render(:partial => 'picture', :locals => {:f => f})
    6         # caso use jquery, inserir a linha abaixo
    7         # $('#pictures').after("#{escape_javascript html}".replace(/NEW_RECORD/g, new_picture_id));
    8         page << %{
    9           var new_picture_id = new Date().getTime()
   10           $('pictures').insert({ bottom: "#{ escape_javascript html }".replace(/NEW_RECORD/g, new_picture_id) });
   11         }
   12       end
   13     end
   14   end
   15 end

Adicionando o método criado, poderemos adicionar vários arquivos para fazer upload:

    1 <h1>New product</h1>
    2
    3 <% form_for(@product, :html => { :multipart => true }) do |f| %>
    4   <%= f.error_messages %>
    5
    6   <p>
    7     <%= f.label :name %><br />
    8     <%= f.text_field :name %>
    9   </p>
   10
   11   <div id="pictures">
   12     <p>
   13       <% f.fields_for :pictures do |pictures_form| %>
   14           <%= render :partial => 'picture', :locals => { :f => pictures_form } %>
   15       <% end %>
   16     </p>
   17   </div>
   18
   19   <%= add_picture f %>
   20
   21   <p>
   22     <%= f.submit 'Create' %>
   23   </p>
   24 <% end %>
   25
   26 <%= link_to 'Back', products_path %>

Ao iniciarmos o servidor (http://localhost:3000/products/new) veremos o link Add Picture.

Não se esqueça de incluir os javascripts no layout <%= javascript_include_tag :all, :cache => true %>

Com isso você tem um ponta pé inicial para trabalhar com atributos aninhados na sua aplicação. Não é difícil usar atributos aninhados com paperclip, que agrega outra função para o model. Você pode por exemplo usar atributos aninhados dentro de um outro model, que está em um outro model e que está em um outro model… Não existe limites para isso :)

UPDATE: Obrigado ao Jésus Lopes que descobriu alguns bugs (na partial e no helper) que já foram devidamente corrigidos ;)

Comentários (5)

Ei cairo que tal você printar umas imagens da tela?
grato!

Não precisa :P

Já que no final vai funcionar tudo bonitinho ;)

Fala Cairo,

Fiz tudo igual seu tutorial, mas não sei pq os campos que adiciono depois, não passam parametros, você tem alguma idéia?

olá Cairo

Existem 3 imagens quebradas nesse tutorial, podia nos ajudar recolocando, pois preciso saber como é esse scaffold.

Obrigado

Poderia arrumas as imagens por favor?

Poste um comentário