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

What's hot

ICING AND FROSTING.pptx
ICING AND FROSTING.pptxICING AND FROSTING.pptx
ICING AND FROSTING.pptx
RichelleDomingo4
 
Method of Cooking 1 - Pan Frying & Deep Frying - chefqtrainer.blogspot.com
Method of Cooking 1 - Pan Frying & Deep Frying - chefqtrainer.blogspot.comMethod of Cooking 1 - Pan Frying & Deep Frying - chefqtrainer.blogspot.com
Method of Cooking 1 - Pan Frying & Deep Frying - chefqtrainer.blogspot.com
Culinary Training Program
 
Cake Making
Cake Making Cake Making
Cake Making
Maxine Walters-Pitt
 
Cooking ( techniques, tools, styles, ingredients, future ) cooking overall , ...
Cooking ( techniques, tools, styles, ingredients, future ) cooking overall , ...Cooking ( techniques, tools, styles, ingredients, future ) cooking overall , ...
Cooking ( techniques, tools, styles, ingredients, future ) cooking overall , ...
Siddhartha Banerjee
 
Why is financial literacy important
Why is financial literacy importantWhy is financial literacy important
Why is financial literacy important
MehulPuri3
 
Money management
Money managementMoney management
Money management
Sivasangari Shanmugam
 
The History Of Baking
The History Of BakingThe History Of Baking
The History Of Baking
Daisygirl
 
Factoring opportunities in Spain, an analysis of companies in the IBEX 35
Factoring opportunities in Spain, an analysis of companies in the IBEX 35Factoring opportunities in Spain, an analysis of companies in the IBEX 35
Factoring opportunities in Spain, an analysis of companies in the IBEX 35
Nina Schmiedt
 
Expression ecrite-sur-le-travail-des-enfants Le texte-narratif 2ème semestre ...
Expression ecrite-sur-le-travail-des-enfants Le texte-narratif 2ème semestre ...Expression ecrite-sur-le-travail-des-enfants Le texte-narratif 2ème semestre ...
Expression ecrite-sur-le-travail-des-enfants Le texte-narratif 2ème semestre ...
mehdirhora
 
Prepare pasta, grains and farinaceous dishes
Prepare pasta, grains and farinaceous dishesPrepare pasta, grains and farinaceous dishes
Prepare pasta, grains and farinaceous dishes
Padme Amidala
 
Paper mache
Paper machePaper mache
Paper mache
Gina Christoffel
 
Behavioral finance (2008)
Behavioral finance (2008)Behavioral finance (2008)
Behavioral finance (2008)
Jam Chaman Shahzad
 
Group 4 M1 MISE EN PLACE EGG.pptx
Group 4 M1 MISE EN PLACE EGG.pptxGroup 4 M1 MISE EN PLACE EGG.pptx
Group 4 M1 MISE EN PLACE EGG.pptx
MerlindaPacquiao
 
Elements & principles of design
Elements & principles of designElements & principles of design
Elements & principles of design
sharmiarchitect
 
Basic Baking
Basic BakingBasic Baking
Basic Baking
Debashis Das
 
Lesson 1 Basic Cooking Methods and Food Preparation Techniques
Lesson 1   Basic Cooking Methods and Food Preparation TechniquesLesson 1   Basic Cooking Methods and Food Preparation Techniques
Lesson 1 Basic Cooking Methods and Food Preparation Techniques
Juvywen
 
Las tle 10 bpp q1_m8-wk8
Las tle 10 bpp q1_m8-wk8Las tle 10 bpp q1_m8-wk8
Las tle 10 bpp q1_m8-wk8
LynnelYap3
 
Python基本資料運算
Python基本資料運算Python基本資料運算
Python基本資料運算
吳錫修 (ShyiShiou Wu)
 

What's hot (18)

ICING AND FROSTING.pptx
ICING AND FROSTING.pptxICING AND FROSTING.pptx
ICING AND FROSTING.pptx
 
Method of Cooking 1 - Pan Frying & Deep Frying - chefqtrainer.blogspot.com
Method of Cooking 1 - Pan Frying & Deep Frying - chefqtrainer.blogspot.comMethod of Cooking 1 - Pan Frying & Deep Frying - chefqtrainer.blogspot.com
Method of Cooking 1 - Pan Frying & Deep Frying - chefqtrainer.blogspot.com
 
Cake Making
Cake Making Cake Making
Cake Making
 
Cooking ( techniques, tools, styles, ingredients, future ) cooking overall , ...
Cooking ( techniques, tools, styles, ingredients, future ) cooking overall , ...Cooking ( techniques, tools, styles, ingredients, future ) cooking overall , ...
Cooking ( techniques, tools, styles, ingredients, future ) cooking overall , ...
 
Why is financial literacy important
Why is financial literacy importantWhy is financial literacy important
Why is financial literacy important
 
Money management
Money managementMoney management
Money management
 
The History Of Baking
The History Of BakingThe History Of Baking
The History Of Baking
 
Factoring opportunities in Spain, an analysis of companies in the IBEX 35
Factoring opportunities in Spain, an analysis of companies in the IBEX 35Factoring opportunities in Spain, an analysis of companies in the IBEX 35
Factoring opportunities in Spain, an analysis of companies in the IBEX 35
 
Expression ecrite-sur-le-travail-des-enfants Le texte-narratif 2ème semestre ...
Expression ecrite-sur-le-travail-des-enfants Le texte-narratif 2ème semestre ...Expression ecrite-sur-le-travail-des-enfants Le texte-narratif 2ème semestre ...
Expression ecrite-sur-le-travail-des-enfants Le texte-narratif 2ème semestre ...
 
Prepare pasta, grains and farinaceous dishes
Prepare pasta, grains and farinaceous dishesPrepare pasta, grains and farinaceous dishes
Prepare pasta, grains and farinaceous dishes
 
Paper mache
Paper machePaper mache
Paper mache
 
Behavioral finance (2008)
Behavioral finance (2008)Behavioral finance (2008)
Behavioral finance (2008)
 
Group 4 M1 MISE EN PLACE EGG.pptx
Group 4 M1 MISE EN PLACE EGG.pptxGroup 4 M1 MISE EN PLACE EGG.pptx
Group 4 M1 MISE EN PLACE EGG.pptx
 
Elements & principles of design
Elements & principles of designElements & principles of design
Elements & principles of design
 
Basic Baking
Basic BakingBasic Baking
Basic Baking
 
Lesson 1 Basic Cooking Methods and Food Preparation Techniques
Lesson 1   Basic Cooking Methods and Food Preparation TechniquesLesson 1   Basic Cooking Methods and Food Preparation Techniques
Lesson 1 Basic Cooking Methods and Food Preparation Techniques
 
Las tle 10 bpp q1_m8-wk8
Las tle 10 bpp q1_m8-wk8Las tle 10 bpp q1_m8-wk8
Las tle 10 bpp q1_m8-wk8
 
Python基本資料運算
Python基本資料運算Python基本資料運算
Python基本資料運算
 

Similar to Ajax nested form and ajax upload in rails

Django Class-based views (Slovenian)
Django Class-based views (Slovenian)Django Class-based views (Slovenian)
Django Class-based views (Slovenian)
Luka Zakrajšek
 
Django Vs Rails
Django Vs RailsDjango Vs Rails
Django Vs Rails
Sérgio Santos
 
Rails 3 overview
Rails 3 overviewRails 3 overview
Rails 3 overview
Yehuda Katz
 
Apostrophe (improved Paris edition)
Apostrophe (improved Paris edition)Apostrophe (improved Paris edition)
Apostrophe (improved Paris edition)
tompunk
 
Virtual Madness @ Etsy
Virtual Madness @ EtsyVirtual Madness @ Etsy
Virtual Madness @ Etsy
Nishan Subedi
 
Aplicacoes dinamicas Rails com Backbone
Aplicacoes dinamicas Rails com BackboneAplicacoes dinamicas Rails com Backbone
Aplicacoes dinamicas Rails com Backbone
Rafael Felix da Silva
 
PHPConf-TW 2012 # Twig
PHPConf-TW 2012 # TwigPHPConf-TW 2012 # Twig
PHPConf-TW 2012 # Twig
Wake Liu
 
Rails 3: Dashing to the Finish
Rails 3: Dashing to the FinishRails 3: Dashing to the Finish
Rails 3: Dashing to the Finish
Yehuda Katz
 
50 Laravel Tricks in 50 Minutes
50 Laravel Tricks in 50 Minutes50 Laravel Tricks in 50 Minutes
50 Laravel Tricks in 50 Minutes
Azim Kurt
 
laravel tricks in 50minutes
laravel tricks in 50minuteslaravel tricks in 50minutes
laravel tricks in 50minutes
Barang CK
 
Dig Deeper into WordPress - WD Meetup Cairo
Dig Deeper into WordPress - WD Meetup CairoDig Deeper into WordPress - WD Meetup Cairo
Dig Deeper into WordPress - WD Meetup Cairo
Mohamed Mosaad
 
Be RESTful (Symfony Camp 2008)
Be RESTful (Symfony Camp 2008)Be RESTful (Symfony Camp 2008)
Be RESTful (Symfony Camp 2008)
Fabien Potencier
 
WordPress plugin #3
WordPress plugin #3WordPress plugin #3
WordPress plugin #3
giwoolee
 
Action View Form Helpers - 1, Season 2
Action View Form Helpers - 1, Season 2Action View Form Helpers - 1, Season 2
Action View Form Helpers - 1, Season 2
RORLAB
 
The Rails Way
The Rails WayThe Rails Way
The Rails Way
Michał Orman
 
Flask – Python
Flask – PythonFlask – Python
Flask – Python
Max Claus Nunes
 
WordPress Theme Design and Development Workshop - Day 3
WordPress Theme Design and Development Workshop - Day 3WordPress Theme Design and Development Workshop - Day 3
WordPress Theme Design and Development Workshop - Day 3
Mizanur Rahaman Mizan
 
اسلاید جلسه ۹ کلاس پایتون برای هکر های قانونی
اسلاید جلسه ۹ کلاس پایتون برای هکر های قانونیاسلاید جلسه ۹ کلاس پایتون برای هکر های قانونی
اسلاید جلسه ۹ کلاس پایتون برای هکر های قانونی
Mohammad Reza Kamalifard
 
OSDC 2009 Rails Turtorial
OSDC 2009 Rails TurtorialOSDC 2009 Rails Turtorial
OSDC 2009 Rails Turtorial
Yi-Ting Cheng
 
Slimme Joomla! Templating Tips en Truuks
Slimme Joomla! Templating Tips en TruuksSlimme Joomla! Templating Tips en Truuks
Slimme Joomla! Templating Tips en Truuks
ThemePartner
 

Similar to Ajax nested form and ajax upload in rails (20)

Django Class-based views (Slovenian)
Django Class-based views (Slovenian)Django Class-based views (Slovenian)
Django Class-based views (Slovenian)
 
Django Vs Rails
Django Vs RailsDjango Vs Rails
Django Vs Rails
 
Rails 3 overview
Rails 3 overviewRails 3 overview
Rails 3 overview
 
Apostrophe (improved Paris edition)
Apostrophe (improved Paris edition)Apostrophe (improved Paris edition)
Apostrophe (improved Paris edition)
 
Virtual Madness @ Etsy
Virtual Madness @ EtsyVirtual Madness @ Etsy
Virtual Madness @ Etsy
 
Aplicacoes dinamicas Rails com Backbone
Aplicacoes dinamicas Rails com BackboneAplicacoes dinamicas Rails com Backbone
Aplicacoes dinamicas Rails com Backbone
 
PHPConf-TW 2012 # Twig
PHPConf-TW 2012 # TwigPHPConf-TW 2012 # Twig
PHPConf-TW 2012 # Twig
 
Rails 3: Dashing to the Finish
Rails 3: Dashing to the FinishRails 3: Dashing to the Finish
Rails 3: Dashing to the Finish
 
50 Laravel Tricks in 50 Minutes
50 Laravel Tricks in 50 Minutes50 Laravel Tricks in 50 Minutes
50 Laravel Tricks in 50 Minutes
 
laravel tricks in 50minutes
laravel tricks in 50minuteslaravel tricks in 50minutes
laravel tricks in 50minutes
 
Dig Deeper into WordPress - WD Meetup Cairo
Dig Deeper into WordPress - WD Meetup CairoDig Deeper into WordPress - WD Meetup Cairo
Dig Deeper into WordPress - WD Meetup Cairo
 
Be RESTful (Symfony Camp 2008)
Be RESTful (Symfony Camp 2008)Be RESTful (Symfony Camp 2008)
Be RESTful (Symfony Camp 2008)
 
WordPress plugin #3
WordPress plugin #3WordPress plugin #3
WordPress plugin #3
 
Action View Form Helpers - 1, Season 2
Action View Form Helpers - 1, Season 2Action View Form Helpers - 1, Season 2
Action View Form Helpers - 1, Season 2
 
The Rails Way
The Rails WayThe Rails Way
The Rails Way
 
Flask – Python
Flask – PythonFlask – Python
Flask – Python
 
WordPress Theme Design and Development Workshop - Day 3
WordPress Theme Design and Development Workshop - Day 3WordPress Theme Design and Development Workshop - Day 3
WordPress Theme Design and Development Workshop - Day 3
 
اسلاید جلسه ۹ کلاس پایتون برای هکر های قانونی
اسلاید جلسه ۹ کلاس پایتون برای هکر های قانونیاسلاید جلسه ۹ کلاس پایتون برای هکر های قانونی
اسلاید جلسه ۹ کلاس پایتون برای هکر های قانونی
 
OSDC 2009 Rails Turtorial
OSDC 2009 Rails TurtorialOSDC 2009 Rails Turtorial
OSDC 2009 Rails Turtorial
 
Slimme Joomla! Templating Tips en Truuks
Slimme Joomla! Templating Tips en TruuksSlimme Joomla! Templating Tips en Truuks
Slimme Joomla! Templating Tips en Truuks
 

More from Tse-Ching Ho

20150516 modern web_conf_tw
20150516 modern web_conf_tw20150516 modern web_conf_tw
20150516 modern web_conf_tw
Tse-Ching Ho
 
Ruby on bioinformatics
Ruby on bioinformaticsRuby on bioinformatics
Ruby on bioinformatics
Tse-Ching Ho
 
mongodb-introduction
mongodb-introductionmongodb-introduction
mongodb-introduction
Tse-Ching Ho
 
devise tutorial - 2011 rubyconf taiwan
devise tutorial - 2011 rubyconf taiwandevise tutorial - 2011 rubyconf taiwan
devise tutorial - 2011 rubyconf taiwan
Tse-Ching Ho
 
Rails-3-app-auto-generator-20100817
Rails-3-app-auto-generator-20100817Rails-3-app-auto-generator-20100817
Rails-3-app-auto-generator-20100817
Tse-Ching Ho
 
model.search: customize your own search logic
model.search: customize your own search logicmodel.search: customize your own search logic
model.search: customize your own search logic
Tse-Ching Ho
 
The Power of Rails 2.3 Engines & Templates
The Power of Rails 2.3 Engines & TemplatesThe Power of Rails 2.3 Engines & Templates
The Power of Rails 2.3 Engines & Templates
Tse-Ching Ho
 
ruby e-commerce
ruby e-commerceruby e-commerce
ruby e-commerce
Tse-Ching Ho
 

More from Tse-Ching Ho (9)

20150516 modern web_conf_tw
20150516 modern web_conf_tw20150516 modern web_conf_tw
20150516 modern web_conf_tw
 
Ruby on bioinformatics
Ruby on bioinformaticsRuby on bioinformatics
Ruby on bioinformatics
 
Webconf2013
Webconf2013Webconf2013
Webconf2013
 
mongodb-introduction
mongodb-introductionmongodb-introduction
mongodb-introduction
 
devise tutorial - 2011 rubyconf taiwan
devise tutorial - 2011 rubyconf taiwandevise tutorial - 2011 rubyconf taiwan
devise tutorial - 2011 rubyconf taiwan
 
Rails-3-app-auto-generator-20100817
Rails-3-app-auto-generator-20100817Rails-3-app-auto-generator-20100817
Rails-3-app-auto-generator-20100817
 
model.search: customize your own search logic
model.search: customize your own search logicmodel.search: customize your own search logic
model.search: customize your own search logic
 
The Power of Rails 2.3 Engines & Templates
The Power of Rails 2.3 Engines & TemplatesThe Power of Rails 2.3 Engines & Templates
The Power of Rails 2.3 Engines & Templates
 
ruby e-commerce
ruby e-commerceruby e-commerce
ruby e-commerce
 

Recently uploaded

Essentials of Automations: Exploring Attributes & Automation Parameters
Essentials of Automations: Exploring Attributes & Automation ParametersEssentials of Automations: Exploring Attributes & Automation Parameters
Essentials of Automations: Exploring Attributes & Automation Parameters
Safe Software
 
9 CEO's who hit $100m ARR Share Their Top Growth Tactics Nathan Latka, Founde...
9 CEO's who hit $100m ARR Share Their Top Growth Tactics Nathan Latka, Founde...9 CEO's who hit $100m ARR Share Their Top Growth Tactics Nathan Latka, Founde...
9 CEO's who hit $100m ARR Share Their Top Growth Tactics Nathan Latka, Founde...
saastr
 
How to Interpret Trends in the Kalyan Rajdhani Mix Chart.pdf
How to Interpret Trends in the Kalyan Rajdhani Mix Chart.pdfHow to Interpret Trends in the Kalyan Rajdhani Mix Chart.pdf
How to Interpret Trends in the Kalyan Rajdhani Mix Chart.pdf
Chart Kalyan
 
"Frontline Battles with DDoS: Best practices and Lessons Learned", Igor Ivaniuk
"Frontline Battles with DDoS: Best practices and Lessons Learned",  Igor Ivaniuk"Frontline Battles with DDoS: Best practices and Lessons Learned",  Igor Ivaniuk
"Frontline Battles with DDoS: Best practices and Lessons Learned", Igor Ivaniuk
Fwdays
 
JavaLand 2024: Application Development Green Masterplan
JavaLand 2024: Application Development Green MasterplanJavaLand 2024: Application Development Green Masterplan
JavaLand 2024: Application Development Green Masterplan
Miro Wengner
 
Columbus Data & Analytics Wednesdays - June 2024
Columbus Data & Analytics Wednesdays - June 2024Columbus Data & Analytics Wednesdays - June 2024
Columbus Data & Analytics Wednesdays - June 2024
Jason Packer
 
zkStudyClub - LatticeFold: A Lattice-based Folding Scheme and its Application...
zkStudyClub - LatticeFold: A Lattice-based Folding Scheme and its Application...zkStudyClub - LatticeFold: A Lattice-based Folding Scheme and its Application...
zkStudyClub - LatticeFold: A Lattice-based Folding Scheme and its Application...
Alex Pruden
 
"Choosing proper type of scaling", Olena Syrota
"Choosing proper type of scaling", Olena Syrota"Choosing proper type of scaling", Olena Syrota
"Choosing proper type of scaling", Olena Syrota
Fwdays
 
Leveraging the Graph for Clinical Trials and Standards
Leveraging the Graph for Clinical Trials and StandardsLeveraging the Graph for Clinical Trials and Standards
Leveraging the Graph for Clinical Trials and Standards
Neo4j
 
Harnessing the Power of NLP and Knowledge Graphs for Opioid Research
Harnessing the Power of NLP and Knowledge Graphs for Opioid ResearchHarnessing the Power of NLP and Knowledge Graphs for Opioid Research
Harnessing the Power of NLP and Knowledge Graphs for Opioid Research
Neo4j
 
Energy Efficient Video Encoding for Cloud and Edge Computing Instances
Energy Efficient Video Encoding for Cloud and Edge Computing InstancesEnergy Efficient Video Encoding for Cloud and Edge Computing Instances
Energy Efficient Video Encoding for Cloud and Edge Computing Instances
Alpen-Adria-Universität
 
GNSS spoofing via SDR (Criptored Talks 2024)
GNSS spoofing via SDR (Criptored Talks 2024)GNSS spoofing via SDR (Criptored Talks 2024)
GNSS spoofing via SDR (Criptored Talks 2024)
Javier Junquera
 
Monitoring and Managing Anomaly Detection on OpenShift.pdf
Monitoring and Managing Anomaly Detection on OpenShift.pdfMonitoring and Managing Anomaly Detection on OpenShift.pdf
Monitoring and Managing Anomaly Detection on OpenShift.pdf
Tosin Akinosho
 
Fueling AI with Great Data with Airbyte Webinar
Fueling AI with Great Data with Airbyte WebinarFueling AI with Great Data with Airbyte Webinar
Fueling AI with Great Data with Airbyte Webinar
Zilliz
 
GraphRAG for LifeSciences Hands-On with the Clinical Knowledge Graph
GraphRAG for LifeSciences Hands-On with the Clinical Knowledge GraphGraphRAG for LifeSciences Hands-On with the Clinical Knowledge Graph
GraphRAG for LifeSciences Hands-On with the Clinical Knowledge Graph
Neo4j
 
Nordic Marketo Engage User Group_June 13_ 2024.pptx
Nordic Marketo Engage User Group_June 13_ 2024.pptxNordic Marketo Engage User Group_June 13_ 2024.pptx
Nordic Marketo Engage User Group_June 13_ 2024.pptx
MichaelKnudsen27
 
Connector Corner: Seamlessly power UiPath Apps, GenAI with prebuilt connectors
Connector Corner: Seamlessly power UiPath Apps, GenAI with prebuilt connectorsConnector Corner: Seamlessly power UiPath Apps, GenAI with prebuilt connectors
Connector Corner: Seamlessly power UiPath Apps, GenAI with prebuilt connectors
DianaGray10
 
Biomedical Knowledge Graphs for Data Scientists and Bioinformaticians
Biomedical Knowledge Graphs for Data Scientists and BioinformaticiansBiomedical Knowledge Graphs for Data Scientists and Bioinformaticians
Biomedical Knowledge Graphs for Data Scientists and Bioinformaticians
Neo4j
 
Overcoming the PLG Trap: Lessons from Canva's Head of Sales & Head of EMEA Da...
Overcoming the PLG Trap: Lessons from Canva's Head of Sales & Head of EMEA Da...Overcoming the PLG Trap: Lessons from Canva's Head of Sales & Head of EMEA Da...
Overcoming the PLG Trap: Lessons from Canva's Head of Sales & Head of EMEA Da...
saastr
 
Programming Foundation Models with DSPy - Meetup Slides
Programming Foundation Models with DSPy - Meetup SlidesProgramming Foundation Models with DSPy - Meetup Slides
Programming Foundation Models with DSPy - Meetup Slides
Zilliz
 

Recently uploaded (20)

Essentials of Automations: Exploring Attributes & Automation Parameters
Essentials of Automations: Exploring Attributes & Automation ParametersEssentials of Automations: Exploring Attributes & Automation Parameters
Essentials of Automations: Exploring Attributes & Automation Parameters
 
9 CEO's who hit $100m ARR Share Their Top Growth Tactics Nathan Latka, Founde...
9 CEO's who hit $100m ARR Share Their Top Growth Tactics Nathan Latka, Founde...9 CEO's who hit $100m ARR Share Their Top Growth Tactics Nathan Latka, Founde...
9 CEO's who hit $100m ARR Share Their Top Growth Tactics Nathan Latka, Founde...
 
How to Interpret Trends in the Kalyan Rajdhani Mix Chart.pdf
How to Interpret Trends in the Kalyan Rajdhani Mix Chart.pdfHow to Interpret Trends in the Kalyan Rajdhani Mix Chart.pdf
How to Interpret Trends in the Kalyan Rajdhani Mix Chart.pdf
 
"Frontline Battles with DDoS: Best practices and Lessons Learned", Igor Ivaniuk
"Frontline Battles with DDoS: Best practices and Lessons Learned",  Igor Ivaniuk"Frontline Battles with DDoS: Best practices and Lessons Learned",  Igor Ivaniuk
"Frontline Battles with DDoS: Best practices and Lessons Learned", Igor Ivaniuk
 
JavaLand 2024: Application Development Green Masterplan
JavaLand 2024: Application Development Green MasterplanJavaLand 2024: Application Development Green Masterplan
JavaLand 2024: Application Development Green Masterplan
 
Columbus Data & Analytics Wednesdays - June 2024
Columbus Data & Analytics Wednesdays - June 2024Columbus Data & Analytics Wednesdays - June 2024
Columbus Data & Analytics Wednesdays - June 2024
 
zkStudyClub - LatticeFold: A Lattice-based Folding Scheme and its Application...
zkStudyClub - LatticeFold: A Lattice-based Folding Scheme and its Application...zkStudyClub - LatticeFold: A Lattice-based Folding Scheme and its Application...
zkStudyClub - LatticeFold: A Lattice-based Folding Scheme and its Application...
 
"Choosing proper type of scaling", Olena Syrota
"Choosing proper type of scaling", Olena Syrota"Choosing proper type of scaling", Olena Syrota
"Choosing proper type of scaling", Olena Syrota
 
Leveraging the Graph for Clinical Trials and Standards
Leveraging the Graph for Clinical Trials and StandardsLeveraging the Graph for Clinical Trials and Standards
Leveraging the Graph for Clinical Trials and Standards
 
Harnessing the Power of NLP and Knowledge Graphs for Opioid Research
Harnessing the Power of NLP and Knowledge Graphs for Opioid ResearchHarnessing the Power of NLP and Knowledge Graphs for Opioid Research
Harnessing the Power of NLP and Knowledge Graphs for Opioid Research
 
Energy Efficient Video Encoding for Cloud and Edge Computing Instances
Energy Efficient Video Encoding for Cloud and Edge Computing InstancesEnergy Efficient Video Encoding for Cloud and Edge Computing Instances
Energy Efficient Video Encoding for Cloud and Edge Computing Instances
 
GNSS spoofing via SDR (Criptored Talks 2024)
GNSS spoofing via SDR (Criptored Talks 2024)GNSS spoofing via SDR (Criptored Talks 2024)
GNSS spoofing via SDR (Criptored Talks 2024)
 
Monitoring and Managing Anomaly Detection on OpenShift.pdf
Monitoring and Managing Anomaly Detection on OpenShift.pdfMonitoring and Managing Anomaly Detection on OpenShift.pdf
Monitoring and Managing Anomaly Detection on OpenShift.pdf
 
Fueling AI with Great Data with Airbyte Webinar
Fueling AI with Great Data with Airbyte WebinarFueling AI with Great Data with Airbyte Webinar
Fueling AI with Great Data with Airbyte Webinar
 
GraphRAG for LifeSciences Hands-On with the Clinical Knowledge Graph
GraphRAG for LifeSciences Hands-On with the Clinical Knowledge GraphGraphRAG for LifeSciences Hands-On with the Clinical Knowledge Graph
GraphRAG for LifeSciences Hands-On with the Clinical Knowledge Graph
 
Nordic Marketo Engage User Group_June 13_ 2024.pptx
Nordic Marketo Engage User Group_June 13_ 2024.pptxNordic Marketo Engage User Group_June 13_ 2024.pptx
Nordic Marketo Engage User Group_June 13_ 2024.pptx
 
Connector Corner: Seamlessly power UiPath Apps, GenAI with prebuilt connectors
Connector Corner: Seamlessly power UiPath Apps, GenAI with prebuilt connectorsConnector Corner: Seamlessly power UiPath Apps, GenAI with prebuilt connectors
Connector Corner: Seamlessly power UiPath Apps, GenAI with prebuilt connectors
 
Biomedical Knowledge Graphs for Data Scientists and Bioinformaticians
Biomedical Knowledge Graphs for Data Scientists and BioinformaticiansBiomedical Knowledge Graphs for Data Scientists and Bioinformaticians
Biomedical Knowledge Graphs for Data Scientists and Bioinformaticians
 
Overcoming the PLG Trap: Lessons from Canva's Head of Sales & Head of EMEA Da...
Overcoming the PLG Trap: Lessons from Canva's Head of Sales & Head of EMEA Da...Overcoming the PLG Trap: Lessons from Canva's Head of Sales & Head of EMEA Da...
Overcoming the PLG Trap: Lessons from Canva's Head of Sales & Head of EMEA Da...
 
Programming Foundation Models with DSPy - Meetup Slides
Programming Foundation Models with DSPy - Meetup SlidesProgramming Foundation Models with DSPy - Meetup Slides
Programming Foundation Models with DSPy - Meetup Slides
 

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/