I've been struggling with this issue all night. I'm trying to upload multiple "Photo" models with Dropzone.js with the paperclip attribute "attachment". Currently I'm using the DropZone-Rails gem (I've tried several versions all with same result).
# photo.rb
class Photo < ActiveRecord::Base
has_attached_file :attachment, styles: { medium: "720x720>", thumb: "360x360>" }, default_url: "/images/:style/missing.png"
validates_attachment_content_type :attachment, content_type: /\Aimage\/.*\Z/
belongs_to :gallery
belongs_to :user
end
As you can see below, I've whitelisted the attachment parameter.
#photos_controller.rb
class PhotosController < ApplicationController
before_action :set_photo, only: [:show, :edit, :update, :destroy]
.........
private
# Use callbacks to share common setup or constraints between actions.
def set_photo
#photo = Photo.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def photo_params
params.require(:photo).permit(:attachment, :gallery_id)
end
DropZone loads as expected, configured here with:
Dropzone.autoDiscover = false;
In my template:
$("#new_photo").dropzone({
// restrict photo size to a maximum 1MB
maxFilesize: 4,
paramName: "photo[attachment]",
addRemoveLinks: true,
uploadMultiple: true,
success: function(file, response){
$(file.previewTemplate).find('.dz-remove').attr('id', response.fileID);
$(file.previewElement).addClass("dz-success");
}
});
var myDropzone = new Dropzone();
My form with DropZone's fallback:
<%= form_for(Photo.new, html: { multipart: true, class: "dropzone"}) do |f| %>
<%= f.hidden_field :gallery_id, :value=>#gallery.id %>
<div class="fallback">
<%= f.file_field :attachment %>
<%= f.submit "Upload" %>
</div>
<% end %>
My log's output for params is (before getting filtered):
Parameters: {"utf8"=>"✓", "authenticity_token"=>"5ftDcdfB1YpL4UF1fupC/I11xRx3HV5GYih5kshO/KSXzSrZS5Iu1wVRhbLFiyfQWQbwP3R5mxeoJLiM+cUJDg==", "photo"=>{"gallery_id"=>"13", "attachment"=>{"0"=>#<ActionDispatch::Http::UploadedFile:0x007f8058e13c48 #tempfile=#<Tempfile:/var/folders/jb/f02hpkhs3813_b6w_bvyxkxw0000gn/T/RackMultipart20170419-16050-8s5zd8.jpg>, #original_filename="624bbbf5b3c8a293950c8ed24a0c4eef.jpg", #content_type="image/jpeg", #headers="Content-Disposition: form-data; name=\"photo[attachment][0]\"; filename=\"624bbbf5b3c8a293950c8ed24a0c4eef.jpg\"\r\nContent-Type: image/jpeg\r\n">}}}
This leads to:
Unpermitted parameter: attachment
{"gallery_id"=>"13"}
Any help would be greatly appreciated. I've searched everything and tried everything and can't seem to make it work. Thank you.
From your model Photo definition, it is clear that you want to upload 1 attachment file per record. But the params you are receiving in your controller are those for uploading multiple attachments.
{ ..., attachment => { 0 => #<ActionDispatch::Http::UploadedFile }, ... }
If you want to permit only 1 attachment in your params, your params should be in form:
{ ..., attachment => #<ActionDispatch::Http::UploadedFile, ... }
Looking at the dropzone options, i would say, try to change uploadMultiple: true to uploadMultiple: false in drop zone configuration. Let me know, if the problem still persists.
Related
I made a Rails 6 app and added a private messaging feature in it using action cable. The chat works fine in chrome and edge but crashes in firefox. Upon investigation in firefox, I think whenever the 'send' button in the create message form is clicked, it seems it reloads the page, disconnecting the current subscription. It says in the terminal that the async job is performed successfully before it gets disconnected. The data (in received(data)) doesn't get console logged in the browser.
I thought I just need to prevent the page to reload once the 'send' button is clicked so I tried placing preventDefault in submit listener in both application.js and conversation_channel.js but didn't work. Also added onclick: false in the submit tag in message form but didn't work also. It's maybe my theory is wrong or maybe I executed the solution wrong.
Please tell me if you need more snippets of my code:
// this is app/javascript/channels/conversation_channel.js
import consumer from "./consumer"
document.addEventListener('turbolinks:load', () => {
const conversation_element = document.getElementById('conversation-id');
const conversation_id = Number(conversation_element.getAttribute('data-conversation-id'));
const input_box = document.getElementById('message-input-box');
const send_button = document.getElementById('send-btn');
// for terminating other subscriptions when connected to a new subscription
consumer.subscriptions.subscriptions.forEach(subs => {
consumer.subscriptions.remove(subs);
});
consumer.subscriptions.create({ channel: "ConversationChannel", conversation_id: conversation_id }, {
connected() {
// Called when the subscription is ready for use on the server
console.log('Connected to conversation id: ' + conversation_id);
send_button.addEventListener('submit', function(e) { e.preventDefault(); });
},
disconnected() {
// Called when the subscription has been terminated by the server
},
received(data) {
// Called when there's incoming data on the websocket for this channel
console.log(data);
const el_user_id = document.getElementById('user-id');
const user_id = Number(el_user_id.getAttribute('data-user-id'));
let html;
user_id === data.message.user_id ? html = data.own_message : html = data.not_own_message;
const messageContainer = document.getElementById('messages-container');
messageContainer.innerHTML += html;
send_button.disabled = false;
input_box.value = '';
}
});
});
this is where the user creates a message: app/views/messages/_form.html.erb
<%= form_with scope: :message, url: item_conversation_messages_path(#conversation.item.id, #conversation.id), local: true, remote: true do |f| %>
<div class="form-inline">
<%= f.hidden_field :conversation_id, value: #conversation.id %>
<%= f.hidden_field :user_id, value: current_user.id %>
<%= f.text_field :content, placeholder: 'Type your message here...', class: 'form-control mr-2', id: 'message-input-box' %>
<%= f.submit 'Send', id: 'send-btn', class: 'btn btn-primary' %>
</div>
<% end %>
It was pointed out by user even_progression in my reddit post https://www.reddit.com/r/rails/comments/ol9ygj/rails_6_action_cable_chat_app_not_working_in/ that I should check the local and remote options in the message form. I set local to false and remote to true and now it works. Idk yet why but I'm about to find out.
I am currently requisition an html partial to be inserted into a modal
$('#user-addTool').on('click', function(e) {
$('#userModal div.modal-content').html('');
var ajaxUrl = "http://" + window.location.host + "/users/new";
e.preventDefault();
e.stopPropagation();
$.get(ajaxUrl, function(data){
$('#userModal div.modal-content').html(data);
initGroupSelector();
initRoleSelector();
$('#role-selector').multiselect('disable');
}, "html");
$('#userModal').modal('show');
});
served by a rails app :
# GET /users/new
# GET /users/new.json
def new
#user = User.new
#user.profile = Profile.new
authorize #user
render partial: 'users/form', layout: false
end
I wonder if I can return both, the partial form html AND a son object ( groups and roles to be used in the client js script ?
thanks for feedback
UPDATE 1 --
Can I send a JSON response this way ?
format.json {
#response = {
"html": { "form": <%= (render partial: 'users/form').to_json.html_safe %> },
"data": { "groups": <%= #groups %> , "roles": <%= #roles %> }
}
render(json: #response, status: :success )
}
In which #group is an Array of Arrays and #roles a Hash of Hashes ?
thanks for advices
I wanted to add confirmation message on link_to function with Ruby.
= link_to 'Reset message', :action=>'reset' ,:confirm=>'Are you sure?'
Any ideas why it's not working?
I might be mistaken but you don't specify a controller along with the :action option. Have you tried the following? Assuming you have a messages resource configured in your route:
link_to 'Reset', message_path(#message), :confirm => 'Are you sure?'
EDIT: Above is deprecated. Rails 4.0 now accepts the prompt as a data attribute. See the doc here (Thanks #Ricky).
link_to 'Reset', message_path(#message), :data => {:confirm => 'Are you sure?'}
First, you should verify that your layout have jquery_ujs.
Best practice to do it by including it in your main application.js:
//= require jquery_ujs
Check that you included application.js in your layout:
= javascript_include_tag :application
While, in development mode, view your source html and verify jquery_ujs.js exists.
Run your server and verify your link tag has data-confirm value, for example:
<a href="/articles/1" data-confirm="Are you sure?" data-method="delete">
If all those steps are correct, everything should work!
Note: check this RailsCast http://railscasts.com/episodes/136-jquery-ajax-revised
Can't remember how this was done in Rails 3, but in Rails 4 you can simply:
<%= link_to 'Reset message', { controller: 'your_controller', action: 'reset' }, data: {confirm: 'Are you sure?'} %>
<%= link_to 'Reset Message', data: {confirm:"Are you sure?"} %>
remember to add the path, between 'reset message' and data
<%= link_to "Delete this article", article_path(article), method: :delete,
data: { confirm: "Are you sure you want to delete the
article?"}, class: "btn btn-xs btn-danger" %>
A button link where article_path is the prefix and (article) is passing the id which is required by the method: :delete method.
The later part of the codes adds a confirmation msg.
Try this:
= link_to 'Reset message', {:action=>'reset'}, :confirm=>'Are you sure?'
or to be more clear
= link_to('Reset message', {:action=>'reset'}, {:confirm=>'Are you sure?'})
Refer http://api.rubyonrails.org/classes/ActionView/Helpers/UrlHelper.html#method-i-link_to
You will see that there are 3 parameters, when you are giving url as options like {:action => ..., :controller => ...}
link_to(body, url_options = {}, html_options = {})
In ruby, if the last parameter in a function call is a hash, you need not wrap it in {} characters (in other words, you can omit that in case, if the hash is the last parameter), so the code you have provided will be interpreted as a function call with only 2 parameters, 'Reset message' string and {:action=>'reset', :confirm=>'Are you sure?'} hash and the :confirm=>'Are you sure?' will be interpreted as a url_option instead of a html_option
Somehow does not work those code only Safari browser So I was involved button...
<%= button_to('', delete_path(), method: "delete", data: { confirm: 'Are you sure?', disable_with: 'loading...' }) %>
Look at your javascript_include_tag and it should work fine:
<%= link_to("Reset message", :method => :reset, :class => 'action', :confirm => 'Are you sure?') %>
watch this railscasts video for better understanding.
http://railscasts.com/episodes/205-unobtrusive-javascript
rails documentation for link_to helper.
http://api.rubyonrails.org/classes/ActionView/Helpers/UrlHelper.html#method-i-link_to
First, we need to understand what Js package respond to this kind of alerts in rails application. So for this, jquery_ujs package is reponsible for showing the alerts in rails.
So you must have jquery & jquery_ujs in your application.js file.
//= require jquery
//= require jquery_ujs
Now, we need to confirm, that application.js file is included in your required layout or not. By default layout file remains in application.html.erb in layout folder of views.
<%= javascript_include_tag 'application' %>
Next the link should have data-confirm & data-method attributes as
<a href="/message/1/reset" data-method="delete" data-confirm="Are you sure?">
In erb, this can be written as,
= link_to 'Reset', message_path(#message), data: {method: 'delete', confirm: 'Are you sure?'}
This should work if everything is aligned in same fashion.
I am trying to have an AJAX implementation of record deletion associated with a button. The problem is that ajax:success event doesn't seem to be triggered in such case.
I have implemented the suggestion from this post: Rails :confirm modifier callback?) but I am uncertain if it's the preferred way.
I was wondering if communal wisdom could help in this case. What's the right approach here?
app/views/competitions/show.html.haml:
%td= button_to 'Delete', contender, remote: true, method: :delete, class: 'delete_contender', confirm: 'Are you sure?' if owns?
app/assets/javascripts/competitions.js:
$(document).ready(function() {
$('.delete_contender').on('confirm:complete', function(e, res) {
if (res) {
$(e.currentTarget).closest('tr').fadeOut();
}
});
});
app/controllers/contenders_controller.rb:
def destroy
#contender = Contender.find(params[:id])
#competition = #contender.competition
#contender.destroy
respond_to do |format|
format.js
format.html { redirect_to #competition, notice: "Contender #{#contender.sn_name} has been deleted" }
format.json { head :no_content }
end
end
The quick answer is: that is not the right approach. The long answer is below.
Instead of using .delete_contender class as an anchor for action binding, I should have used "form[data-remote]" since *button_to* helper generates a form. Also, there is no need to keep the JS hook inside the asset pipeline, it's better to move it to the views and convert to CoffeeScript. The Rails 3 style solution is:
app/views/competitions/show.html.haml:
%td= button_to 'Delete', contender, remote: true, method: :delete, confirm: 'Are you sure?' if owns?
app/views/competitions/destroy.js.coffee:
jQuery ->
$("form[data-remote]").on "ajax:success", (e, data, status, xhr) ->
$(e.currentTarget).closest('tr').fadeOut()
app/controllers/contenders_controller.rb:
respond_to :js, only: :destroy
def destroy
#contender = Contender.find(params[:id])
#competition = #contender.competition
#contender.destroy
end
// main htm page from where aja x call happened
<div id ="vote_count_<%=answer.id %>" >
<%= render :partial => '/votes/count', :locals =>{ :answer => answer} %>//render a partial
<div id="wait" style=" float:center">// for loading image
</div>
</div>
// partial page :- count
<div class ="look6">
<%= image_tag("thumbs_up.jpg", :alt => "like",:value => answer.id,:id => 'like',:class => 'vote')%>// image tag for like reload during ajax call
(<%= Vote.count(answer.id,'like')%>)// no of like vote
| <%= image_tag("thumbs_down.jpg", :alt => "unlike",:value => answer.id,:id =>'unlike',:class => 'vote',:onclick=>"return vote()") %>// image tag for unlike reload during ajax call
(<%= Vote.count(answer.id,'unlike')%>)// no of unlike vote
</div>
// ajax function :-
<script type="text/javascript" >
function showLoadingImage()
{
$('#wait').append('<div id="loading-image"><%= image_tag("ajax-loader.gif", :alt=>"Loading..." )%></div>');// for ajax loading
}
in first page:
//.vote is a claas name
$(".vote").click(function(){
alert("hi");
var answer_id =$(this).attr("value");// for ans id
alert(answer_id);
showLoadingImage();// call loading image function
var result = $(this).attr("id");// whether like or unlike
$.ajax({
cache: false,
//path for controller
url: '/create_vote/'+answer_id,
data: "result="+result,// data for ajax call
complete: function(){
$('#loading-image').remove();// remove loading image
}
});
cache: false;
return false;
});
</script>
//ontroller:
def create_vote
#vote = Vote.new // create new vot
#vote.user_id = current_user.id// user id
#vote.answer_id = params[:id]// answer id
#vote.result = params[:result] == 'like' ? '1':'0'// like or unlike
answer = Answer.find(params[:id])// answer id find
if #vote.save// save vote
#message = "thanks"// message
else
#message = "sorry"// mesage
end
#vote_count = Vote.count(params[:id], params[:result])// total vote
respond_to do |format|
format.js { render '/votes/create_vote.js.erb', :locals => {:result =>params[:result],:answer =>answer}}// result return back
end
end
///votes/create_vote.js.erb
$("#vote_count_<%=#vote.answer_id %>").html("<%= escape_javascript(render :partial => '/votes/count', :locals => {:result => result ,:answer =>answer }) %>")// render js part
//_count partial
<%= image_tag("thumbs_up.jpg", :alt => "like",:value => answer.id,:id => 'like',:class => 'vote')%>
(<%= Vote.count(answer.id,'like')%>)
| <%= image_tag("thumbs_down.jpg", :alt => "unlike",:value => answer.id,:id =>'unlike',:class => 'vote',:onclick=>"return vote()") %>
(<%= Vote.count(answer.id,'unlike')%>)
use:- $(".vote").live("click", function(){
in place of : $(".vote").click(function(){