SlideShare a Scribd company logo
Ajax nested form &
Ajax upload in Rails
   何澤清 Tse-Ching Ho
     2012/08/21
About


• https://github.com/tsechingho
• https://twitter.com/tsechingho
• https://facebook.com/tsechingho
Demo Code
https://github.com/tsechingho/ajax-tutorial
Prerequisite Work
Gems supposed to
        be well understood

• jquery-rails
• anjlab-bootstrap-rails
• simple_form
• carrierwave
• mini_magick / rmagick
Ground rails project

• Have two models associated with has_many
• Have one model mounted with carrierwave’s
 uploader

• Render 'form' in views of new and edit
• Use respond_with for each action of controller
• Layout with twitter bootstrap
Twitter Bootstrap Modal
Handle Feedback
of .ajaxSubmit() via Modal


   modal    modal       modal      modal




                        Error
                       feedback    Success
           .feedback
                         modal    feedback
Creature & CreaturePhoto

                                                       require 'file_size_validator'

                                                       class CreaturePhoto < ActiveRecord::Base
class Creature < ActiveRecord::Base
                                                         attr_accessible :content_type, :file_name, :file_size,
 attr_accessible :characteristic, :place_of_origin,
                                                       :creature_id, :source, :source_cache, :remove_source
:popular_name, :scientific_name, :animal_handbook_ids
                                                        validates :source,
 validates :popular_name, presence: true
                                                         file_size: {
                                                           maximum: 3.megabytes.to_i
 has_many :animal_handbook_creatures
                                                         }
 has_many :animal_handbooks,
through: :animal_handbook_creatures
                                                        mount_uploader :source,
 has_many :photos, class_name: 'CreaturePhoto'
                                                       CreaturePhotoUploader, mount_on: :file_name
                                                        delegate :url, :current_path, :size, :filename, to: :source
 def name
  popular_name
                                                        belongs_to :creature
 end
end
                                                        before_save :update_attributes_with_source
                                                       end
creatures_controller.rb
class CreaturesController < ApplicationController
 before_filter :load_creature, only: [:show, :edit, :update, :destroy]

 respond_to :html

 def edit
  render 'edit_modal', layout: false if request.xhr?
 end

 def update
  @creature.update_attributes params[:creature]
  if @creature.valid?
    flash[:notice] = 'Creature was successfully updated.'
  end
  respond_with @creature do |format|
    format.html {
      if @creature.valid?
        load_creatures
        render partial: 'table', locals: { creatures: @creatures }
      else
        render 'edit_modal', layout: false
      end
    } if request.xhr?
  end
  flash.discard :notice if request.xhr?
 end
end
twitter_bootstrap_helper.rb
def iconed_link_to(text, url, options = {})
 icon_class = options.delete(:icon_class)
 link_to url, options do
   content_tag(:i, nil, class: icon_class) << ' ' << text
 end
end

def link_to_edit(url, options = {})
 icon_class = options.delete(:icon_class) || 'icon-edit'
 default_options = { title: t('helpers.edit'), class: 'btn', icon_class: icon_class }
 iconed_link_to nil, url, default_options.deep_merge(options)
end

def link_to_edit_modal(url, modal_id)
 default_options = { remote: true, data: { target: modal_id, toggle: 'modal', type:
'html' }, class: 'btn modal-open' }
 link_to_edit url, default_options
end
creatures/index.html.erb
<article id="creature-list">
 <header>
  <h1><%= t('.title') %></h1>
 </header>

 <%= render_list class: 'nav nav-tabs' do |li|
  li << [link_to_open_modal(t('helpers.new'), new_creature_path, '#creature-
modal'), { class: 'action' }]
 end %>

 <%= render 'table', creatures: @creatures %>

 <nav role="pagination">
 </nav>
</article>

<div class="modal hide fade" id="creature-modal"></div>
creatures/_table.html.erb
<table class="table table-striped table-bordered">
 <tr>
   <th><%= Creature.human_attribute_name :popular_name %></th>
   <th><%= Creature.human_attribute_name :scientific_name %></th>
   <th><%= Creature.human_attribute_name :place_of_origin %></th>
   <th><%= Creature.human_attribute_name :characteristic %></th>
   <th><%= t('helpers.actions') %></th>
 </tr>
 <% creatures.each do |creature| %>
   <tr>
    <td><%= creature.popular_name %></td>
    <td><%= creature.scientific_name %></td>
    <td><%= creature.place_of_origin %></td>
    <td><%= creature.characteristic %></td>
    <td class="btn-group">
      <%= link_to_show creature_path(creature) %>
      <%= link_to_edit_modal edit_creature_path(creature), '#creature-modal' %>
      <%= link_to_destroy creature_path(creature) %>
    </td>
   </tr>
 <% end %>
</table>
creatures/edit_modal.html.erb
<%= simple_form_for @creature, remote: true, html: { data: { type: 'html' }, class:
'form-horizontal' } do |f| %>
  <div class="modal-header">
   <button type="button" class="close" data-dismiss="modal">×</button>
   <h3><%= t('.title') %></h3>
  </div>

 <div class="modal-body">
  <%= render 'form', f: f %>
 </div>

 <div class="modal-footer">
  <a href="#" class="btn" data-dismiss="modal"><%= t('helpers.close') %></a>
  <%= f.button :submit, name: nil, class: 'btn-primary' %>
 </div>
<% end %>
modal.js.coffee
$ ->
 $.modal ||= {}

 $.modal.appendFeedback = (modal, data) ->
  $('<div>').addClass('feedback hide').html(data).appendTo(modal)

 $.modal.replaceFeedback = (modal) ->
  modal.html(modal.children('.feedback').html())
  $.modal.enableChosen()

 $.modal.replaceTable = (table_id, modal = $(this)) ->
  feedback_table = modal.find('.table')
  table = $(table_id).find('.table')
  table.html(feedback_table.html())
  modal.find('.feedback').remove().end()
     .modal('hide')
  table.effect('shake')
  return true
creatures.js.coffee
$ ->
 $('#creature-list')
   .on 'ajax:success', 'a.modal-open', (event, data, status, xhr) ->
    modal = $($(this).attr('data-target'))
    modal.html(data)
    $.modal.enableChosen()
   .on 'ajax:error', '.a.modal-open', (event, xhr, status, error) ->
    modal = $($(this).attr('data-target'))
    $.modal.showErrorModal(status, error, modal)

 $('#creature-modal')
  .on 'ajax:success', '.simple_form', (event, data, status, xhr) ->
   modal = $(this).parent()
   $.modal.appendFeedback(modal, data)
   if modal.find('.feedback .alert-error').size() > 0
     $.modal.replaceFeedback(modal)
     return true
   table_id = '#creature-list'
   $.modal.replaceTable(table_id, modal)
  .on 'ajax:error', '.simple_form', (event, xhr, status, error) ->
   modal = $('#creature-modal')
   $.modal.showErrorModal(status, error, modal)
application.js

//= require jquery
//= require jquery-ui
//= require jquery_ujs
//= require bootstrap
//= require chosen-jquery
//= require modal
//= require_tree .
Key Points
• Use respond_with
• Render 'modal' specific files
• Render partial files
• Via data attributes
• Define rails ajax callbacks
• Use namespace for javascript methods
• Catch ajax callback in div container if data-type
  is :html
Ajax Nested Form
How To

• Concepts
  • Save template in data attributes
  • DOM manipulation
• https://github.com/nathanvda/cocoon
• gem 'cocoon'
creature.rb
class Creature < ActiveRecord::Base
  attr_accessible :characteristic, :place_of_origin, :popular_name, :scientific_name,
:animal_handbook_ids, :photos_attributes

 validates :popular_name, presence: true

 has_many :animal_handbook_creatures
 has_many :animal_handbooks, through: :animal_handbook_creatures
 has_many :photos, class_name: 'CreaturePhoto'
 accepts_nested_attributes_for :photos, allow_destroy: true, reject_if: proc
{ |obj| obj.blank? }

 def name
  popular_name
 end
end
twitter_bootstrap_helper.rb
module TwitterBootstrapHelper
 def iconed_link_to_add_association(text, *args)
  args << {} if args.size < 2
  icon_class = args.last.delete(:icon_class) || 'icon-plus'
  default_options = { title: t('helpers.add'), class: 'btn' }
  args.last.deep_merge! default_options
  link_to_add_association *args do
    content_tag(:i, nil, class: icon_class) << ' ' << text
  end
 end

 def iconed_link_to_remove_association(text, *args)
  args << {} if args.size < 2
  icon_class = args.last.delete(:icon_class) || 'icon-remove'
  default_options = { title: t('helpers.remove'), class: 'btn' }
  args.last.deep_merge! default_options
  link_to_remove_association *args do
    content_tag(:i, nil, class: icon_class) << ' ' << text
  end
 end
end
creatures/_form.html.erb
<%= f.error_notification %>

<div class="form-inputs">
 <%= f.input :popular_name %>
 <%= f.input :scientific_name %>
 <%= f.input :place_of_origin %>
 <%= f.input :characteristic, input_html: { size: '20x5' } %>
 <%= f.association :animal_handbooks, input_html: { class: 'chzn-select' } %>
</div>


<h3><%= t('.creature_photos') %></h3>
<div class="form-inputs form-inline">
 <%= render 'creature_photos/field_labels', creature_form: f %>
 <%= f.simple_fields_for :photos do |f2| %>
  <%= render 'creature_photos/fields', f: f2 %>
 <% end %>
</div>

<%= iconed_link_to_add_association t('helpers.add'),
 f,
 :photos,
 data: {
  :'association-insertion-node' => '.form-inputs.form-inline',
  :'association-insertion-method' => :append
 },
 partial: 'creature_photos/fields',
 render_options: {
  locals: { }
 } %>
creatures/_fields.html.erb


<%= field_set_tag nil, class: 'creature-fields row-fluid nested-form-hidden-label nested-fields' do %>
 <%= f.input :popular_name, wrapper_html: { class: 'span2' }, input_html: { class: 'span12' } %>
 <%= f.input :scientific_name, wrapper_html: { class: 'span2' }, input_html: { class: 'span12' } %>
 <%= f.input :place_of_origin, wrapper_html: { class: 'span2' }, input_html: { class: 'span12' } %>
 <%= f.input :characteristic, as: :string, wrapper_html: { class: 'span4' }, input_html: { class: 'span12' } %>
 <div class="control-group actions span2">
  <%= iconed_link_to_remove_association nil, f %>
 </div>
<% end %>
application.js

//= require jquery
//= require jquery-ui
//= require jquery_ujs
//= require bootstrap
//= require chosen-jquery
//= require cocoon
//= require modal
//= require_tree .
Key Points
• accepts_nested_attributes_for :photos, allow_destroy:
  true

• attr_accessible :photos_attributes
• Render partial file
• Use link_to_add_association helper
• Use link_to_remove_association helper
• Add 'nested-fields' class to container tag of nested item
• Require cocoon javascript
Ajax Upload
               It’s impossible
since browsers forbid for security reason.
    It’s possible if we cheat browsers.
How To

• Concepts
  • iFrame Transport
  • rack middleware to modify request header
• https://github.com/leppert/remotipart
• gem 'remotipart'
iFrame Transport
http://www.alfajango.com/blog/ajax-file-uploads-with-the-iframe-method/
application.js

//= require jquery
//= require jquery-ui
//= require jquery_ujs
//= require bootstrap
//= require chosen-jquery
//= require cocoon
// Since XMLHttpRequest (AJAX) standard has no support for file uploads,
// use iframe-transport method of remotipart gem for ajax file upload.
//= require jquery.remotipart
//= require modal
//= require_tree .
Other ways?
• iFrame
  • https://github.com/blueimp/jQuery-File-Upload
• Flash
  • http://www.uploadify.com
• Form Data
  • http://hacks.mozilla.org/2010/07/firefox-4-
    formdata-and-the-new-file-url-object/
References
•   http://www.alfajango.com/blog/rails-3-remote-links-
    and-forms/

•   http://www.alfajango.com/blog/rails-3-remote-links-
    and-forms-data-type-with-jquery/

•   http://railscasts.com/episodes/196-nested-model-
    form-revised

•   http://www.alfajango.com/blog/ajax-file-uploads-with-
    the-iframe-method/

•   http://os.alfajango.com/remotipart/
THANKS

More Related Content

PDF
Презентация спектакля "Затерянный мир" Презентация спектакля пример. Презента...
PDF
MidCamp 2016 - Demystifying AJAX Callback Commands in Drupal 8
PPT
jQuery and AJAX with Rails
PDF
Writing and Publishing Puppet Modules - PuppetConf 2014
PDF
Google Developer Day: State of Ajax
PDF
20150516 modern web_conf_tw
PDF
Ruby on bioinformatics
PDF
Webconf2013
Презентация спектакля "Затерянный мир" Презентация спектакля пример. Презента...
MidCamp 2016 - Demystifying AJAX Callback Commands in Drupal 8
jQuery and AJAX with Rails
Writing and Publishing Puppet Modules - PuppetConf 2014
Google Developer Day: State of Ajax
20150516 modern web_conf_tw
Ruby on bioinformatics
Webconf2013

More from Tse-Ching Ho (6)

PDF
mongodb-introduction
PDF
devise tutorial - 2011 rubyconf taiwan
PDF
Rails-3-app-auto-generator-20100817
PDF
model.search: customize your own search logic
PDF
The Power of Rails 2.3 Engines & Templates
PDF
ruby e-commerce
mongodb-introduction
devise tutorial - 2011 rubyconf taiwan
Rails-3-app-auto-generator-20100817
model.search: customize your own search logic
The Power of Rails 2.3 Engines & Templates
ruby e-commerce
Ad

Recently uploaded (20)

PPTX
VMware vSphere Foundation How to Sell Presentation-Ver1.4-2-14-2024.pptx
PDF
Shreyas Phanse Resume: Experienced Backend Engineer | Java • Spring Boot • Ka...
PDF
Advanced methodologies resolving dimensionality complications for autism neur...
PPTX
MYSQL Presentation for SQL database connectivity
PPTX
A Presentation on Artificial Intelligence
PDF
Bridging biosciences and deep learning for revolutionary discoveries: a compr...
PDF
Blue Purple Modern Animated Computer Science Presentation.pdf.pdf
PDF
Peak of Data & AI Encore- AI for Metadata and Smarter Workflows
PDF
Agricultural_Statistics_at_a_Glance_2022_0.pdf
PPTX
Effective Security Operations Center (SOC) A Modern, Strategic, and Threat-In...
PDF
Review of recent advances in non-invasive hemoglobin estimation
PDF
Modernizing your data center with Dell and AMD
PDF
NewMind AI Monthly Chronicles - July 2025
PPTX
KOM of Painting work and Equipment Insulation REV00 update 25-dec.pptx
PDF
Approach and Philosophy of On baking technology
PPTX
PA Analog/Digital System: The Backbone of Modern Surveillance and Communication
PDF
TokAI - TikTok AI Agent : The First AI Application That Analyzes 10,000+ Vira...
PPTX
Big Data Technologies - Introduction.pptx
PPTX
Cloud computing and distributed systems.
PDF
Network Security Unit 5.pdf for BCA BBA.
VMware vSphere Foundation How to Sell Presentation-Ver1.4-2-14-2024.pptx
Shreyas Phanse Resume: Experienced Backend Engineer | Java • Spring Boot • Ka...
Advanced methodologies resolving dimensionality complications for autism neur...
MYSQL Presentation for SQL database connectivity
A Presentation on Artificial Intelligence
Bridging biosciences and deep learning for revolutionary discoveries: a compr...
Blue Purple Modern Animated Computer Science Presentation.pdf.pdf
Peak of Data & AI Encore- AI for Metadata and Smarter Workflows
Agricultural_Statistics_at_a_Glance_2022_0.pdf
Effective Security Operations Center (SOC) A Modern, Strategic, and Threat-In...
Review of recent advances in non-invasive hemoglobin estimation
Modernizing your data center with Dell and AMD
NewMind AI Monthly Chronicles - July 2025
KOM of Painting work and Equipment Insulation REV00 update 25-dec.pptx
Approach and Philosophy of On baking technology
PA Analog/Digital System: The Backbone of Modern Surveillance and Communication
TokAI - TikTok AI Agent : The First AI Application That Analyzes 10,000+ Vira...
Big Data Technologies - Introduction.pptx
Cloud computing and distributed systems.
Network Security Unit 5.pdf for BCA BBA.
Ad

Ajax nested form and ajax upload in rails

  • 1. Ajax nested form & Ajax upload in Rails 何澤清 Tse-Ching Ho 2012/08/21
  • 5. Gems supposed to be well understood • jquery-rails • anjlab-bootstrap-rails • simple_form • carrierwave • mini_magick / rmagick
  • 6. Ground rails project • Have two models associated with has_many • Have one model mounted with carrierwave’s uploader • Render 'form' in views of new and edit • Use respond_with for each action of controller • Layout with twitter bootstrap
  • 8. Handle Feedback of .ajaxSubmit() via Modal modal modal modal modal Error feedback Success .feedback modal feedback
  • 9. Creature & CreaturePhoto require 'file_size_validator' class CreaturePhoto < ActiveRecord::Base class Creature < ActiveRecord::Base attr_accessible :content_type, :file_name, :file_size, attr_accessible :characteristic, :place_of_origin, :creature_id, :source, :source_cache, :remove_source :popular_name, :scientific_name, :animal_handbook_ids validates :source, validates :popular_name, presence: true file_size: { maximum: 3.megabytes.to_i has_many :animal_handbook_creatures } has_many :animal_handbooks, through: :animal_handbook_creatures mount_uploader :source, has_many :photos, class_name: 'CreaturePhoto' CreaturePhotoUploader, mount_on: :file_name delegate :url, :current_path, :size, :filename, to: :source def name popular_name belongs_to :creature end end before_save :update_attributes_with_source end
  • 10. creatures_controller.rb class CreaturesController < ApplicationController before_filter :load_creature, only: [:show, :edit, :update, :destroy] respond_to :html def edit render 'edit_modal', layout: false if request.xhr? end def update @creature.update_attributes params[:creature] if @creature.valid? flash[:notice] = 'Creature was successfully updated.' end respond_with @creature do |format| format.html { if @creature.valid? load_creatures render partial: 'table', locals: { creatures: @creatures } else render 'edit_modal', layout: false end } if request.xhr? end flash.discard :notice if request.xhr? end end
  • 11. twitter_bootstrap_helper.rb def iconed_link_to(text, url, options = {}) icon_class = options.delete(:icon_class) link_to url, options do content_tag(:i, nil, class: icon_class) << ' ' << text end end def link_to_edit(url, options = {}) icon_class = options.delete(:icon_class) || 'icon-edit' default_options = { title: t('helpers.edit'), class: 'btn', icon_class: icon_class } iconed_link_to nil, url, default_options.deep_merge(options) end def link_to_edit_modal(url, modal_id) default_options = { remote: true, data: { target: modal_id, toggle: 'modal', type: 'html' }, class: 'btn modal-open' } link_to_edit url, default_options end
  • 12. creatures/index.html.erb <article id="creature-list"> <header> <h1><%= t('.title') %></h1> </header> <%= render_list class: 'nav nav-tabs' do |li| li << [link_to_open_modal(t('helpers.new'), new_creature_path, '#creature- modal'), { class: 'action' }] end %> <%= render 'table', creatures: @creatures %> <nav role="pagination"> </nav> </article> <div class="modal hide fade" id="creature-modal"></div>
  • 13. creatures/_table.html.erb <table class="table table-striped table-bordered"> <tr> <th><%= Creature.human_attribute_name :popular_name %></th> <th><%= Creature.human_attribute_name :scientific_name %></th> <th><%= Creature.human_attribute_name :place_of_origin %></th> <th><%= Creature.human_attribute_name :characteristic %></th> <th><%= t('helpers.actions') %></th> </tr> <% creatures.each do |creature| %> <tr> <td><%= creature.popular_name %></td> <td><%= creature.scientific_name %></td> <td><%= creature.place_of_origin %></td> <td><%= creature.characteristic %></td> <td class="btn-group"> <%= link_to_show creature_path(creature) %> <%= link_to_edit_modal edit_creature_path(creature), '#creature-modal' %> <%= link_to_destroy creature_path(creature) %> </td> </tr> <% end %> </table>
  • 14. creatures/edit_modal.html.erb <%= simple_form_for @creature, remote: true, html: { data: { type: 'html' }, class: 'form-horizontal' } do |f| %> <div class="modal-header"> <button type="button" class="close" data-dismiss="modal">×</button> <h3><%= t('.title') %></h3> </div> <div class="modal-body"> <%= render 'form', f: f %> </div> <div class="modal-footer"> <a href="#" class="btn" data-dismiss="modal"><%= t('helpers.close') %></a> <%= f.button :submit, name: nil, class: 'btn-primary' %> </div> <% end %>
  • 15. modal.js.coffee $ -> $.modal ||= {} $.modal.appendFeedback = (modal, data) -> $('<div>').addClass('feedback hide').html(data).appendTo(modal) $.modal.replaceFeedback = (modal) -> modal.html(modal.children('.feedback').html()) $.modal.enableChosen() $.modal.replaceTable = (table_id, modal = $(this)) -> feedback_table = modal.find('.table') table = $(table_id).find('.table') table.html(feedback_table.html()) modal.find('.feedback').remove().end() .modal('hide') table.effect('shake') return true
  • 16. creatures.js.coffee $ -> $('#creature-list') .on 'ajax:success', 'a.modal-open', (event, data, status, xhr) -> modal = $($(this).attr('data-target')) modal.html(data) $.modal.enableChosen() .on 'ajax:error', '.a.modal-open', (event, xhr, status, error) -> modal = $($(this).attr('data-target')) $.modal.showErrorModal(status, error, modal) $('#creature-modal') .on 'ajax:success', '.simple_form', (event, data, status, xhr) -> modal = $(this).parent() $.modal.appendFeedback(modal, data) if modal.find('.feedback .alert-error').size() > 0 $.modal.replaceFeedback(modal) return true table_id = '#creature-list' $.modal.replaceTable(table_id, modal) .on 'ajax:error', '.simple_form', (event, xhr, status, error) -> modal = $('#creature-modal') $.modal.showErrorModal(status, error, modal)
  • 17. application.js //= require jquery //= require jquery-ui //= require jquery_ujs //= require bootstrap //= require chosen-jquery //= require modal //= require_tree .
  • 18. Key Points • Use respond_with • Render 'modal' specific files • Render partial files • Via data attributes • Define rails ajax callbacks • Use namespace for javascript methods • Catch ajax callback in div container if data-type is :html
  • 20. How To • Concepts • Save template in data attributes • DOM manipulation • https://github.com/nathanvda/cocoon • gem 'cocoon'
  • 21. creature.rb class Creature < ActiveRecord::Base attr_accessible :characteristic, :place_of_origin, :popular_name, :scientific_name, :animal_handbook_ids, :photos_attributes validates :popular_name, presence: true has_many :animal_handbook_creatures has_many :animal_handbooks, through: :animal_handbook_creatures has_many :photos, class_name: 'CreaturePhoto' accepts_nested_attributes_for :photos, allow_destroy: true, reject_if: proc { |obj| obj.blank? } def name popular_name end end
  • 22. twitter_bootstrap_helper.rb module TwitterBootstrapHelper def iconed_link_to_add_association(text, *args) args << {} if args.size < 2 icon_class = args.last.delete(:icon_class) || 'icon-plus' default_options = { title: t('helpers.add'), class: 'btn' } args.last.deep_merge! default_options link_to_add_association *args do content_tag(:i, nil, class: icon_class) << ' ' << text end end def iconed_link_to_remove_association(text, *args) args << {} if args.size < 2 icon_class = args.last.delete(:icon_class) || 'icon-remove' default_options = { title: t('helpers.remove'), class: 'btn' } args.last.deep_merge! default_options link_to_remove_association *args do content_tag(:i, nil, class: icon_class) << ' ' << text end end end
  • 23. creatures/_form.html.erb <%= f.error_notification %> <div class="form-inputs"> <%= f.input :popular_name %> <%= f.input :scientific_name %> <%= f.input :place_of_origin %> <%= f.input :characteristic, input_html: { size: '20x5' } %> <%= f.association :animal_handbooks, input_html: { class: 'chzn-select' } %> </div> <h3><%= t('.creature_photos') %></h3> <div class="form-inputs form-inline"> <%= render 'creature_photos/field_labels', creature_form: f %> <%= f.simple_fields_for :photos do |f2| %> <%= render 'creature_photos/fields', f: f2 %> <% end %> </div> <%= iconed_link_to_add_association t('helpers.add'), f, :photos, data: { :'association-insertion-node' => '.form-inputs.form-inline', :'association-insertion-method' => :append }, partial: 'creature_photos/fields', render_options: { locals: { } } %>
  • 24. creatures/_fields.html.erb <%= field_set_tag nil, class: 'creature-fields row-fluid nested-form-hidden-label nested-fields' do %> <%= f.input :popular_name, wrapper_html: { class: 'span2' }, input_html: { class: 'span12' } %> <%= f.input :scientific_name, wrapper_html: { class: 'span2' }, input_html: { class: 'span12' } %> <%= f.input :place_of_origin, wrapper_html: { class: 'span2' }, input_html: { class: 'span12' } %> <%= f.input :characteristic, as: :string, wrapper_html: { class: 'span4' }, input_html: { class: 'span12' } %> <div class="control-group actions span2"> <%= iconed_link_to_remove_association nil, f %> </div> <% end %>
  • 25. application.js //= require jquery //= require jquery-ui //= require jquery_ujs //= require bootstrap //= require chosen-jquery //= require cocoon //= require modal //= require_tree .
  • 26. Key Points • accepts_nested_attributes_for :photos, allow_destroy: true • attr_accessible :photos_attributes • Render partial file • Use link_to_add_association helper • Use link_to_remove_association helper • Add 'nested-fields' class to container tag of nested item • Require cocoon javascript
  • 27. Ajax Upload It’s impossible since browsers forbid for security reason. It’s possible if we cheat browsers.
  • 28. How To • Concepts • iFrame Transport • rack middleware to modify request header • https://github.com/leppert/remotipart • gem 'remotipart'
  • 30. application.js //= require jquery //= require jquery-ui //= require jquery_ujs //= require bootstrap //= require chosen-jquery //= require cocoon // Since XMLHttpRequest (AJAX) standard has no support for file uploads, // use iframe-transport method of remotipart gem for ajax file upload. //= require jquery.remotipart //= require modal //= require_tree .
  • 31. Other ways? • iFrame • https://github.com/blueimp/jQuery-File-Upload • Flash • http://www.uploadify.com • Form Data • http://hacks.mozilla.org/2010/07/firefox-4- formdata-and-the-new-file-url-object/
  • 32. References • http://www.alfajango.com/blog/rails-3-remote-links- and-forms/ • http://www.alfajango.com/blog/rails-3-remote-links- and-forms-data-type-with-jquery/ • http://railscasts.com/episodes/196-nested-model- form-revised • http://www.alfajango.com/blog/ajax-file-uploads-with- the-iframe-method/ • http://os.alfajango.com/remotipart/