Xây dựng form Multiple File Upload sử dụng gem cocoon và CarrierWave
Lời mở đầu Ở Tutorial này mình sẽ hướng dẫn các bạn xây dựng form upload 1 lúc nhiều ảnh sử dụng kỹ thuật nested attributes . Ích lợi của tutorial này : Upload nhiều file cùng lúc Thêm hoặc remove input fields một cách thoải mái Xem các ảnh trước khi được upload Khi xảy ra lỗi có thể ...
Lời mở đầu
Ở Tutorial này mình sẽ hướng dẫn các bạn xây dựng form upload 1 lúc nhiều ảnh sử dụng kỹ thuật nested attributes .
Ích lợi của tutorial này :
- Upload nhiều file cùng lúc
- Thêm hoặc remove input fields một cách thoải mái
- Xem các ảnh trước khi được upload
- Khi xảy ra lỗi có thể lưu lại cái field đã nhập hay cái ảnh đã chọn.
Chuẩn bị
Ta có 2 bảng như sau :
class Image < ApplicationRecord belongs_to :target, polymorphic: true mount_uploader :image, ImageUploader, reject_if: proc{ |param| param[:image].blank? && param[:image_cache].blank? && param[:id].blank? }, allow_destroy: true end class Project < ApplicationRecord belongs_to :category has_many :images accepts_nested_attributes_for :images, allow_destroy: true
Gem
gem "carrierwave" gem "cocoon" // hỗ trợ nested form
Config
app/assets/js/application.js //= require cocoon
Chúng ta bắt tay vào code
Chúng ta lấy params ở view như sau :
// Projects_Controller def project_params params.require(:project).permit images_attributes: [:id, :image, :image_cache, :_destroy] end
Tiếp theo ở view ta tạo 1 view là new.htmt.erb:
<%= form_for @project do |f| %> ... <div class="form-group"> <%= f.label :images, class: "col-md-4 control-label"%> <div class="col-md-4"> <table class="user-photo-form"> <%= f.fields_for :images do |image| %> <%= render "image_fields", f: image %> <% end %> <%= link_to_add_association (t "add_a_photo"), f, :images, class: "btn btn-default" %> </table> </div> </div> ... <% f.submit "Save "%>
Sau đó ta tạo 1 Partial _image_fields.html.erb (image là 1 trường của bảng 1 images )
<tr class="nested-fields"> // class này dùng để gem cocoon nhận biết đây là phần để nó append vào mỗi khi ấn nút <td> <%= f.file_field :image %> <%= f.hidden_field :image_cache, value: f.object.image_cache %> </td> <td class="thumb"> // để js nhận biết append vào các preview image <% if f.object.image.url.present? %> <%= image_tag f.object.image.url %> <% end %> </td> <td> <%= link_to_remove_association (t "remove"), f %> </td> </tr>
Phần cuối cùng , chúng ta chỉ cần tạo 1 file project.js.coffee như sau :
$ -> onAddFile = (event) -> file = event.target.files[0] url = URL.createObjectURL(file) thumbContainer = $(this).parent().siblings('td.thumb') if thumbContainer.find('img').length == 0 thumbContainer.append '<img src="' + url + '" />' else thumbContainer.find('img').attr 'src', url $('input[type=file]').each -> $(this).change onAddFile $('body').on 'cocoon:after-insert', (e, addedPartial) -> $('input[type=file]', addedPartial).change onAddFile // Nói vs cocoon nơi để insert partial $('a.add_fields').data 'association-insertion-method', 'append' $('a.add_fields').data 'association-insertion-node', 'table.user-photo-form tbody'
Vậy là xong , rất đơn giản phải ko