SlideShare uma empresa Scribd logo
1 de 33
Baixar para ler offline
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

Mais conteúdo relacionado

Mais procurados

Multiple Choice Questions on JAVA (object oriented programming) bank 8 -- int...
Multiple Choice Questions on JAVA (object oriented programming) bank 8 -- int...Multiple Choice Questions on JAVA (object oriented programming) bank 8 -- int...
Multiple Choice Questions on JAVA (object oriented programming) bank 8 -- int...Kuntal Bhowmick
 
Java interfaces & abstract classes
Java interfaces & abstract classesJava interfaces & abstract classes
Java interfaces & abstract classesShreyans Pathak
 
Scrollable Updatable
Scrollable UpdatableScrollable Updatable
Scrollable Updatableleminhvuong
 
Erd perpustakaan
Erd perpustakaanErd perpustakaan
Erd perpustakaanNae Nay
 
Database Management - Lecture 2 - SQL select, insert, update and delete
Database Management - Lecture 2 - SQL select, insert, update and deleteDatabase Management - Lecture 2 - SQL select, insert, update and delete
Database Management - Lecture 2 - SQL select, insert, update and deleteAl-Mamun Sarkar
 
レンダリングを意識したパフォーマンスチューニング
レンダリングを意識したパフォーマンスチューニングレンダリングを意識したパフォーマンスチューニング
レンダリングを意識したパフォーマンスチューニングHayato Mizuno
 
Java Database Connectivity (JDBC)
Java Database Connectivity (JDBC)Java Database Connectivity (JDBC)
Java Database Connectivity (JDBC)Pooja Talreja
 
Iterator Design Pattern
Iterator Design PatternIterator Design Pattern
Iterator Design PatternVarun Arora
 
Java collections concept
Java collections conceptJava collections concept
Java collections conceptkumar gaurav
 
Create List Relationships by Using Unique and Lookup Columns - SharePoint 201...
Create List Relationships by Using Unique and Lookup Columns - SharePoint 201...Create List Relationships by Using Unique and Lookup Columns - SharePoint 201...
Create List Relationships by Using Unique and Lookup Columns - SharePoint 201...EPC Group
 
vb.net Constructor and destructor
vb.net Constructor and destructorvb.net Constructor and destructor
vb.net Constructor and destructorsuraj pandey
 
Android App Development - 05 Action bar
Android App Development - 05 Action barAndroid App Development - 05 Action bar
Android App Development - 05 Action barDiego Grancini
 
Command Design Pattern
Command Design PatternCommand Design Pattern
Command Design PatternShahriar Hyder
 

Mais procurados (20)

Multiple Choice Questions on JAVA (object oriented programming) bank 8 -- int...
Multiple Choice Questions on JAVA (object oriented programming) bank 8 -- int...Multiple Choice Questions on JAVA (object oriented programming) bank 8 -- int...
Multiple Choice Questions on JAVA (object oriented programming) bank 8 -- int...
 
Java interfaces & abstract classes
Java interfaces & abstract classesJava interfaces & abstract classes
Java interfaces & abstract classes
 
Scrollable Updatable
Scrollable UpdatableScrollable Updatable
Scrollable Updatable
 
Bootstrap latihan
Bootstrap latihanBootstrap latihan
Bootstrap latihan
 
Collections in Java Notes
Collections in Java NotesCollections in Java Notes
Collections in Java Notes
 
Erd perpustakaan
Erd perpustakaanErd perpustakaan
Erd perpustakaan
 
Database Management - Lecture 2 - SQL select, insert, update and delete
Database Management - Lecture 2 - SQL select, insert, update and deleteDatabase Management - Lecture 2 - SQL select, insert, update and delete
Database Management - Lecture 2 - SQL select, insert, update and delete
 
レンダリングを意識したパフォーマンスチューニング
レンダリングを意識したパフォーマンスチューニングレンダリングを意識したパフォーマンスチューニング
レンダリングを意識したパフォーマンスチューニング
 
Anonymous functions in JavaScript
Anonymous functions in JavaScriptAnonymous functions in JavaScript
Anonymous functions in JavaScript
 
Java Database Connectivity (JDBC)
Java Database Connectivity (JDBC)Java Database Connectivity (JDBC)
Java Database Connectivity (JDBC)
 
Iterator Design Pattern
Iterator Design PatternIterator Design Pattern
Iterator Design Pattern
 
Java collections concept
Java collections conceptJava collections concept
Java collections concept
 
Oracle: Functions
Oracle: FunctionsOracle: Functions
Oracle: Functions
 
Create List Relationships by Using Unique and Lookup Columns - SharePoint 201...
Create List Relationships by Using Unique and Lookup Columns - SharePoint 201...Create List Relationships by Using Unique and Lookup Columns - SharePoint 201...
Create List Relationships by Using Unique and Lookup Columns - SharePoint 201...
 
Linq
LinqLinq
Linq
 
vb.net Constructor and destructor
vb.net Constructor and destructorvb.net Constructor and destructor
vb.net Constructor and destructor
 
Java Collections Framework
Java Collections FrameworkJava Collections Framework
Java Collections Framework
 
Collection framework
Collection frameworkCollection framework
Collection framework
 
Android App Development - 05 Action bar
Android App Development - 05 Action barAndroid App Development - 05 Action bar
Android App Development - 05 Action bar
 
Command Design Pattern
Command Design PatternCommand Design Pattern
Command Design Pattern
 

Semelhante a 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
 
Rails 3 overview
Rails 3 overviewRails 3 overview
Rails 3 overviewYehuda 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 @ EtsyNishan Subedi
 
Aplicacoes dinamicas Rails com Backbone
Aplicacoes dinamicas Rails com BackboneAplicacoes dinamicas Rails com Backbone
Aplicacoes dinamicas Rails com BackboneRafael Felix da Silva
 
PHPConf-TW 2012 # Twig
PHPConf-TW 2012 # TwigPHPConf-TW 2012 # Twig
PHPConf-TW 2012 # TwigWake Liu
 
Rails 3: Dashing to the Finish
Rails 3: Dashing to the FinishRails 3: Dashing to the Finish
Rails 3: Dashing to the FinishYehuda Katz
 
laravel tricks in 50minutes
laravel tricks in 50minuteslaravel tricks in 50minutes
laravel tricks in 50minutesBarang CK
 
50 Laravel Tricks in 50 Minutes
50 Laravel Tricks in 50 Minutes50 Laravel Tricks in 50 Minutes
50 Laravel Tricks in 50 MinutesAzim Kurt
 
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 CairoMohamed 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 #3giwoolee
 
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 2RORLAB
 
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 3Mizanur Rahaman Mizan
 
اسلاید جلسه ۹ کلاس پایتون برای هکر های قانونی
اسلاید جلسه ۹ کلاس پایتون برای هکر های قانونیاسلاید جلسه ۹ کلاس پایتون برای هکر های قانونی
اسلاید جلسه ۹ کلاس پایتون برای هکر های قانونیMohammad Reza Kamalifard
 
OSDC 2009 Rails Turtorial
OSDC 2009 Rails TurtorialOSDC 2009 Rails Turtorial
OSDC 2009 Rails TurtorialYi-Ting Cheng
 
Slimme Joomla! Templating Tips en Truuks
Slimme Joomla! Templating Tips en TruuksSlimme Joomla! Templating Tips en Truuks
Slimme Joomla! Templating Tips en TruuksThemePartner
 

Semelhante a 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
 
laravel tricks in 50minutes
laravel tricks in 50minuteslaravel tricks in 50minutes
laravel tricks in 50minutes
 
50 Laravel Tricks in 50 Minutes
50 Laravel Tricks in 50 Minutes50 Laravel Tricks in 50 Minutes
50 Laravel Tricks in 50 Minutes
 
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
 

Mais de Tse-Ching Ho

20150516 modern web_conf_tw
20150516 modern web_conf_tw20150516 modern web_conf_tw
20150516 modern web_conf_twTse-Ching Ho
 
Ruby on bioinformatics
Ruby on bioinformaticsRuby on bioinformatics
Ruby on bioinformaticsTse-Ching Ho
 
mongodb-introduction
mongodb-introductionmongodb-introduction
mongodb-introductionTse-Ching Ho
 
devise tutorial - 2011 rubyconf taiwan
devise tutorial - 2011 rubyconf taiwandevise tutorial - 2011 rubyconf taiwan
devise tutorial - 2011 rubyconf taiwanTse-Ching Ho
 
Rails-3-app-auto-generator-20100817
Rails-3-app-auto-generator-20100817Rails-3-app-auto-generator-20100817
Rails-3-app-auto-generator-20100817Tse-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 logicTse-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 & TemplatesTse-Ching Ho
 

Mais de 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
 

Último

Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...DianaGray10
 
Apidays New York 2024 - APIs in 2030: The Risk of Technological Sleepwalk by ...
Apidays New York 2024 - APIs in 2030: The Risk of Technological Sleepwalk by ...Apidays New York 2024 - APIs in 2030: The Risk of Technological Sleepwalk by ...
Apidays New York 2024 - APIs in 2030: The Risk of Technological Sleepwalk by ...apidays
 
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot TakeoffStrategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoffsammart93
 
Architecting Cloud Native Applications
Architecting Cloud Native ApplicationsArchitecting Cloud Native Applications
Architecting Cloud Native ApplicationsWSO2
 
Corporate and higher education May webinar.pptx
Corporate and higher education May webinar.pptxCorporate and higher education May webinar.pptx
Corporate and higher education May webinar.pptxRustici Software
 
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemkeProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemkeProduct Anonymous
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerThousandEyes
 
Introduction to Multilingual Retrieval Augmented Generation (RAG)
Introduction to Multilingual Retrieval Augmented Generation (RAG)Introduction to Multilingual Retrieval Augmented Generation (RAG)
Introduction to Multilingual Retrieval Augmented Generation (RAG)Zilliz
 
Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...
Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...
Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...apidays
 
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, AdobeApidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobeapidays
 
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers:  A Deep Dive into Serverless Spatial Data and FMECloud Frontiers:  A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FMESafe Software
 
Platformless Horizons for Digital Adaptability
Platformless Horizons for Digital AdaptabilityPlatformless Horizons for Digital Adaptability
Platformless Horizons for Digital AdaptabilityWSO2
 
Vector Search -An Introduction in Oracle Database 23ai.pptx
Vector Search -An Introduction in Oracle Database 23ai.pptxVector Search -An Introduction in Oracle Database 23ai.pptx
Vector Search -An Introduction in Oracle Database 23ai.pptxRemote DBA Services
 
[BuildWithAI] Introduction to Gemini.pdf
[BuildWithAI] Introduction to Gemini.pdf[BuildWithAI] Introduction to Gemini.pdf
[BuildWithAI] Introduction to Gemini.pdfSandro Moreira
 
AWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of TerraformAWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of TerraformAndrey Devyatkin
 
Elevate Developer Efficiency & build GenAI Application with Amazon Q​
Elevate Developer Efficiency & build GenAI Application with Amazon Q​Elevate Developer Efficiency & build GenAI Application with Amazon Q​
Elevate Developer Efficiency & build GenAI Application with Amazon Q​Bhuvaneswari Subramani
 
WSO2's API Vision: Unifying Control, Empowering Developers
WSO2's API Vision: Unifying Control, Empowering DevelopersWSO2's API Vision: Unifying Control, Empowering Developers
WSO2's API Vision: Unifying Control, Empowering DevelopersWSO2
 
Finding Java's Hidden Performance Traps @ DevoxxUK 2024
Finding Java's Hidden Performance Traps @ DevoxxUK 2024Finding Java's Hidden Performance Traps @ DevoxxUK 2024
Finding Java's Hidden Performance Traps @ DevoxxUK 2024Victor Rentea
 

Último (20)

Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
 
Apidays New York 2024 - APIs in 2030: The Risk of Technological Sleepwalk by ...
Apidays New York 2024 - APIs in 2030: The Risk of Technological Sleepwalk by ...Apidays New York 2024 - APIs in 2030: The Risk of Technological Sleepwalk by ...
Apidays New York 2024 - APIs in 2030: The Risk of Technological Sleepwalk by ...
 
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot TakeoffStrategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
 
Architecting Cloud Native Applications
Architecting Cloud Native ApplicationsArchitecting Cloud Native Applications
Architecting Cloud Native Applications
 
Understanding the FAA Part 107 License ..
Understanding the FAA Part 107 License ..Understanding the FAA Part 107 License ..
Understanding the FAA Part 107 License ..
 
Corporate and higher education May webinar.pptx
Corporate and higher education May webinar.pptxCorporate and higher education May webinar.pptx
Corporate and higher education May webinar.pptx
 
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemkeProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
 
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected Worker
 
Introduction to Multilingual Retrieval Augmented Generation (RAG)
Introduction to Multilingual Retrieval Augmented Generation (RAG)Introduction to Multilingual Retrieval Augmented Generation (RAG)
Introduction to Multilingual Retrieval Augmented Generation (RAG)
 
Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...
Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...
Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...
 
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, AdobeApidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
 
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers:  A Deep Dive into Serverless Spatial Data and FMECloud Frontiers:  A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
 
Platformless Horizons for Digital Adaptability
Platformless Horizons for Digital AdaptabilityPlatformless Horizons for Digital Adaptability
Platformless Horizons for Digital Adaptability
 
Vector Search -An Introduction in Oracle Database 23ai.pptx
Vector Search -An Introduction in Oracle Database 23ai.pptxVector Search -An Introduction in Oracle Database 23ai.pptx
Vector Search -An Introduction in Oracle Database 23ai.pptx
 
[BuildWithAI] Introduction to Gemini.pdf
[BuildWithAI] Introduction to Gemini.pdf[BuildWithAI] Introduction to Gemini.pdf
[BuildWithAI] Introduction to Gemini.pdf
 
AWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of TerraformAWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of Terraform
 
Elevate Developer Efficiency & build GenAI Application with Amazon Q​
Elevate Developer Efficiency & build GenAI Application with Amazon Q​Elevate Developer Efficiency & build GenAI Application with Amazon Q​
Elevate Developer Efficiency & build GenAI Application with Amazon Q​
 
WSO2's API Vision: Unifying Control, Empowering Developers
WSO2's API Vision: Unifying Control, Empowering DevelopersWSO2's API Vision: Unifying Control, Empowering Developers
WSO2's API Vision: Unifying Control, Empowering Developers
 
Finding Java's Hidden Performance Traps @ DevoxxUK 2024
Finding Java's Hidden Performance Traps @ DevoxxUK 2024Finding Java's Hidden Performance Traps @ DevoxxUK 2024
Finding Java's Hidden Performance Traps @ DevoxxUK 2024
 

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/