Dair
Dair

Reputation: 77

ajax doesn't respond - create.js.erb

I'm working with jquery-file-upload-rails gem to upload multiple images with preview on my form. File upload is working, but the partial with new photos renders only after browser refresh! Maybe, someone can see, what is wrong.

I'm calling file uploader to load photo images in project's edit page:

= f.fields_for :photos, Photo.new do |ff|
  = ff.file_field :img_file, multiple: true, name: "project[photos_attributes][][img_file]", id: 'new_photo'

Photo controller:

  def create
    @project = Project.find(params[:project_id])
    @photo = @project.photos.create(params[:photo].permit(:img_file))
    respond_to do |format|
      format.js
      format.html
    end
  end

photos.js.coffee to make the jquery-file-uploader-rails gem working:

jQuery ->
  $('#new_photo').fileupload
    dataType: 'script'

create.js.erb

$('#photos').html("<%= j render(@photo) %>");

_photo.html.haml

.photo
  - @project.photos.each do |photo|
    = image_tag photo.img_file.thumb
    = link_to 'Remove', photo, data: { confirm: "Are you sure?" }, method: :delete

So I can upload photos. But my ajax doesn't work, even if I put only alert('help'); in my create.js.erb, it doesn't respond. What am I doing wrong? Many thanks in advance!

UPDATE Current situation (with help of Rich Peck):

My models:

class Project < ActiveRecord::Base
  has_many :photos, dependent: :destroy
  accepts_nested_attributes_for :photos, allow_destroy: true
end

class Photo < ActiveRecord::Base
  belongs_to :project
  validates :img_file, presence: true
  mount_uploader :img_file, ImageUploader
end

Projects controller:

  def edit
    @project = Project.find(params[:id])
    @project.photos.build
  end

  def update
    @project = Project.find(params[:id])
    @project.update(project_params)
  end

params.require(:project).permit(photos_attributes: [:img_file])

/projects/edit

  = form_for @project do |f|
    = f.fields_for :photos do |p|
      = p.file_field :img_file, multiple: true, id: 'new_photo'       
  #photos
    = render 'photos/photo'

/photos/_photo

.photo
  = image_tag photo.img_file.thumb if photo.img_file?
  = link_to 'Remove', photo, data: { confirm: "Are you sure?" }, method: :delete

/photos/create.js.erb

$('#photos').html("<%= j render(@photo) %>");

/javascripts/photos.js.coffee

jQuery ->
  $('#new_photo').fileupload
    dataType: 'script'

Upvotes: 1

Views: 811

Answers (3)

Richard Peck
Richard Peck

Reputation: 76784

Several issues.

#app/controllers/projects_controller.rb
class ProjectsController < ApplicationController
   respond_to :js, :html

   def new
      @project = Project.new
      @project.photos.build #-> this will replace Photo.new in fields_for
   end

   def create
      @project = Project.new project_params
      @project.save
      respond_with @project
   end

   private

   def project_params
      params.require(:project).permit(photos_attributes: [:img_file])
   end
end 

#app/views/projects/new.haml
= form_for @project do |f|
    = f.fields_for :photos do |ff|
       = ff.file_field :img_file, multiple: true, id: 'new_photo'

When using nested attributes, you should never be declaring the name attribute of your nested fields. Not only is it against convention, but it also leaves the door open to both hacking & bugs.

If you are using nested attributes, you need to be passing the @project object first, which means the form will either be passing to projects#update or projects#create... not photos#create that you have now.

I would also strongly recommend using the responders gem, it will clear up your code massively.

--

If you wanted to create a new Photo object on its own, you'll want to use the following:

#config/routes.rb
resources :projects do
   resources :photos #-> url.com/projects/:project_id/photos/new
end

#app/controllers/photos_controller.rb
class PhotosController < ApplicationController
   respond_to :js, :html

   def new
      @project = Project.find params[:project_id]
      @photo   = @project.photos.new
   end

   def create
      @project = Project.find params[:project_id]
      @photo   = @project.photos.new photo_params
      @photo.save
      respond_with @photo
   end

   private

   def photo_params
      params.require(:photo).permit(:img_file)
   end
end

#app/views/photos/new.haml
= form_for @photo do |f|
   = f.file_field :img_field

Your JS code is incorrect...

$('#photos').html("<%= j render(@photo) %>");

By default, Rails handles "collections" by calling _[collection_singular_name].html.erb - in your case _photo.html.erb. It loops through the collection to show this each time. To fix yours, just remove the loop from your partial:

#app/views/photos/_photo.html.erb
= image_tag photo.img_file.thumb
= link_to 'Remove', photo, data: { confirm: "Are you sure?" }, method: :delete

Solution

Decide whether you're creating a project or photo.

If you are creating a photo, you should use a separate photos controller (outlined above). If you're creating a new project, you should use nested attributes properly. We've used JQuery-File-Upload multiple times before (you can sign up for free its only a test app):

enter image description here

Your code structure looks okay, only the controller & JS which have issues.


Update

To add a new photo through a project's edit form, just use the following:

#app/controllers/projects_controller.rb
class ProjectsController < ApplicationController
   def edit
      @project = Project.find params[:id]
      @project.photos.build
   end

   def update
      @project = Project.find params[:id]
      @project.update project_params
   end

   private

   def project_params
      params.require(:project).permit(photos_attributes: [:img_file])
   end
end

#app/views/projects/edit.html.erb
<%= form_for @project do |f| %>
   <%= f.fields_for :photos do |p| %>
      <%= p.file_field :img_file %>
   <% end %>
   <%= f.submit %>
<% end %>

Upvotes: 0

Developer
Developer

Reputation: 6243

For ajax file upload to work in jquery-file-upload gem

you have to add :html => { :multipart => true} to your form

<%= form_for Upload.new, :html => { :multipart => true} } do |f| %> ... <%end%>

Please see the jQuery-File-Upload example form for reference

Upvotes: 0

tillmo
tillmo

Reputation: 616

use

$('#photos').html(escape_javascript("<%= j render(@photo) %>"));

see http://api.rubyonrails.org/classes/ActionView/Helpers/JavaScriptHelper.html#method-i-escape_javascript

Upvotes: 0

Related Questions