Turbo and Datatables initialization problem - ruby-on-rails

I am using rails 6 to load a datatables with values. My sidebar looks like this:
<li class="nav-item" >
<%= link_to users_path, {class: 'nav-link', data: {turbo: true} } do %>
<i class="nav-icon icon-user " aria-hidden="true"></i>All Users
<% end %>
</li>
and my datatables is initialized with :
$(document).on('turbo:load', function() {
$('#users_data_table').DataTable( {
columnDefs: [
{
targets: 4,
className: 'text-center',
}
],
stateSave: true,
"order": [[3, 'desc']],
"pagingType": "full_numbers",
"dom": '<lf<t>ip>',
bProcessing: true
......
Examples with rails and Datatables are all over the place, eg: https://www.driftingruby.com/episodes/datatables
My problem:
When I click the link with data {turbo:true}, I get the following error:
DataTables warning: table id=users_data_table - Invalid JSON response.
If I disable turbo with data: {turbo:false}, datatable is initialized correctly.
Have you seen this before? Because it is driving me crazy. Why does turbo have to be disabled for this to work? I am totally baffled.

Related

Rails 7 accessing pinned libraries

importmap.rb includes
pin "javascript-autocomplete", to: "https://ga.jspm.io/npm:javascript-autocomplete#1.0.5/auto-complete.js"
And, following the rails guides, application.js was amended with
import "javascript-autocomplete"
although uncertainty about the syntax remains. Also attempted, as the script defines var autoComplete was
import autoComplete from "javascript-autocomplete"
in either instance, while the header shows:
<script type="importmap" data-turbo-track="reload">{
"imports": {
"application": "/assets/application-333b944449a4c540424142c36f01f97feebd58f2f41d10565ecfde32cb315110.js",
"#hotwired/turbo-rails": "/assets/turbo.min-e5023178542f05fc063cd1dc5865457259cc01f3fba76a28454060d33de6f429.js",
"#hotwired/stimulus": "/assets/stimulus.min-900648768bd96f3faeba359cf33c1bd01ca424ca4d2d05f36a5d8345112ae93c.js",
"#hotwired/stimulus-loading": "/assets/stimulus-loading-1fc59770fb1654500044afd3f5f6d7d00800e5be36746d55b94a2963a7a228aa.js",
"javascript-autocomplete": "https://ga.jspm.io/npm:javascript-autocomplete#1.0.5/auto-complete.js",
[...]
}
the body has
<%= javascript_tag do %>
var demo1 = new autoComplete({
[...]
});
<% end %>
which fails to run: Uncaught ReferenceError: autoComplete is not defined
so the script is not being accessed.
So where is this lacking?

How can use js.erb and json.erb templates in webpacker?

The serviceworker-rails gem has a nice example of why I want to do this. tldr I can generate the manifest using asset helpers and loops.
// app/javascripts/packs/manifest.json.erb
<% icon_sizes = Rails.configuration.serviceworker.icon_sizes %>
{
"name": "My Progressive Rails App",
"short_name": "Progressive",
"start_url": "/",
"icons": [
<% icon_sizes.map { |s| "#{s}x#{s}" }.each.with_index do |dim, i| %>
{
"src": "<%= image_path "serviceworker-rails/heart-#{dim}.png" %>",
"sizes": "<%= dim %>",
"type": "image/png"
}<%= i == (icon_sizes.length - 1) ? '' : ',' %>
<% end %>
],
"theme_color": "#000000",
"background_color": "#FFFFFF",
"display": "fullscreen",
"orientation": "portrait"
}
I also want to inject other rubyland configs into my packfiles such as public keys and other runtime app settings that the js bundle should have. Default rails handles this case really well with erb. How can I do basically the same thing using webpack instead of sprockets?
I tried putting a json.erb in app/javascripts/packs but no go.
To have erb support, try rails webpacker:install:erb
I had the same issue and eventually rendered it using a dedicated controller for the manifest, which also had the advantage of not having to tweak the fingerprinted name and cache headers as the middleware in serviceworker-rails does.

I can't upload files > 1MB using dropzonejs, ruby-on-rails and puma

I am trying to make a web application where people can upload their files, which will be stored on the hard drive then. I am using dropzonejs to handle the file uploads and ruby-on-rails to create my web application.
The problem is that whenever I try to upload a file that is bigger than 1 MB it just hangs on 99.9999% and never gets uploaded fully.
My form is:
<%= form_tag '/upload', html: {multipart: true}, class: "dropzone" , id: "media-dropzone" do %>
<div class="fallback">
<%= file_field_tag "files", multiple: true%>
</div>
<% end %>
The associated javascript is:
<script src="/assets/dropzone.js" type="text/javascript">
var TOKEN=$('meta[name="csrf-token"]').attr('content');
Dropzone.autoDiscover = false;
var dz = new Dropzone("div#mydropzone",{
url: "/upload_file/upload",
autoProcessQueue:false,
uploadMultiple:true,
addRemoveLinks:true,
maxFiles:2,
parallelUploads:2,
acceptedFiles: text/xml,
params:{
'authenticity_token': TOKEN
} });
$('form').submit(function(e) {
if(dz.getQueuedFiles().length > 0) {
e.preventDefault();
dz.processQueue();
}
});
</script>
My rails server runs puma 3.9.1
EDIT
A picture of the hanging upload
Thanks to #Pavel, I remembered to install nginx, and this fixed my problem

How to get webpack2 and underscore-template loader + babel to work without getting "Module build failed: SyntaxError: 'with' in strict mode (5:0)"

I have a underscore template loader in my webpack2 config that is transpiled with babel. It fails at compile time because with is used in the code compiled code. Here is the relevant part in my loaders in webpack.config.js:
I have this section under loaders:
{
test: /\.html$/,
use: [
{
loader: 'babel-loader',
query: {
presets: [
['es2015', { modules: false }],
'es2016',
'es2017',
'stage-3',
],
},
},
{
loader: 'ejs-loader',
},
],
};
This is what I want and I get:
ERROR in ./src/table/row.html
Module build failed: SyntaxError: 'with' in strict mode (5:0)
3 | var __t, __p = '', __e = _.escape, __j = Array.prototype.join;
4 | function print() { __p += __j.call(arguments, '') }
> 5 | with (obj) {
| ^
6 |
7 | _.each(tableKeys, (k) => { ;
8 | __p += '\n <td>' +
If I remove the babel part completely it works but with ES6 code not transpiled:
{
test: /\.html$/,
use: [
{
loader: 'ejs-loader',
},
],
};
I have also seen this question about removing strict mode and have tried several things related to es2015 applying strict. I think I have tried every solution in that question including hotpatching workaround and I still get the same error. In the end i tried this:
{
test: /\.html$/,
use: [
{
loader: 'babel-loader',
query: {
presets: [
],
},
},
{
loader: 'ejs-loader',
},
],
};
I though this should do the same as without the bable pass, but I get the same error here. Somehow without any presets I get the same error.
EDIT
I have also tried to work around it by passing variable in query and I have made that work with ejs-loader, however I'm not looking for a solution where all the templates need changing.
I have made a repository which illustrates the problem. The master branch has babel-loader commented out and works with with while the transpile branch will have compile errors even though { modules: false } is passed and I have a branch called transpile-no-presets where all presets in package.json is removed and the error is still showing.
By default Underscore .template put your data in the scope using a with statement. See Underscore docs.
I think the cleanest solution is to instruct your ejs-loader to not compile to with statements but to use a temporary variable instead:
{
loader: 'ejs-loader?variable=data',
},
...and change your templates to reference the temporary variable.
From:
<ul>
<% _.each(test, (n) => { %>
<li>This is line number <%- n %></li>
<% }); %>
</ul>
to:
<ul>
<% _.each(data.test, (n) => { %>
<li>This is line number <%- n %></li>
<% }); %>
</ul>
Use version 1.0 of underscore-template-loader instead.

Multiple files upload with Rails 3 and paperclip on heroku

I need an interface on my Rails 3 app to upload multiple files to Amazon S3 (because i'm on heroku), possibly with progress bars.
I've easily managed how to set up paperclip and upload single files, but i'm really lost now on how to go ahead.
Please can you give me some advices? It's 2 days i'm searching across all the internet, but i can't find a working solution
** EDIT **
I really can't understand... I'm going mad 'cause I'm losing too many hours on this... please help me.
If I try to open the example app cited by Johnny I only get this (and in my app it is the same):
Where is the UI?
Is there something wrong on my browser?
** EDIT 2 **
Here on GitHub you can find my testapp... please can you explain me why the damn upload UI is not showing up? Thanks!
** EDIT 3 **
Thank you very much Johnny, i wasn't aware of the fact that jquery and prototype can't live together.
Now the plugin is showing up correctly, but as a try to upload something it creates a new "upload" record, but its attachment field is blank, and the files are not on s3.
This is what the console is saying:
Started POST "/uploads" for 127.0.0.1 at 2011-06-27 16:17:22 +0200
Processing by UploadsController#create as JSON
Parameters: {"utf8"=>"✓", "authenticity_token"=>"GesRBTiZR1f2LV/bAeAdxWqF++gxcDJw4pPGStYGsH8=", "upload"=>{"attachment"=>[#<ActionDispatch::Http::UploadedFile:0x000001032834b8 #original_filename="animal-tiger-66550.jpg", #content_type="image/jpeg", #headers="Content-Disposition: form-data; name=\"upload[attachment][]\"; filename=\"animal-tiger-66550.jpg\"\r\nContent-Type: image/jpeg\r\n", #tempfile=#<File:/var/folders/Qj/QjEqvUUNGTmuki5SXOaaG++++TI/-Tmp-/RackMultipart20110627-1818-1syiex9>>]}}
AREL (0.5ms) INSERT INTO "uploads" ("attachment", "created_at", "updated_at", "attachment_file_name", "attachment_content_type", "attachment_file_size", "attachment_updated_at") VALUES (NULL, '2011-06-27 14:17:23.049136', '2011-06-27 14:17:23.049136', NULL, NULL, NULL, NULL)
[paperclip] Saving attachments.
Completed 200 OK in 64ms (Views: 4.2ms | ActiveRecord: 0.7ms)
You can look at jQuery-File-Upload. Demo here and rails 3/Paperclip setup here.
Edit: As #apneadiving mentioned, the library has been updated to version 5. The script you have is for verison 4. You should try modifying this to work with PaperClip. Copy-pasting the majority of the example code into my app (with a few modifications) worked for me:
#app/public/javascripts/application.js
$(function () {
// Initialize the jQuery File Upload widget:
$('#fileupload').fileupload();
// Load existing files:
$.getJSON($('#fileupload form').prop('action'), function (files) {
var fu = $('#fileupload').data('fileupload');
fu._adjustMaxNumberOfFiles(-files.length);
fu._renderDownload(files)
.appendTo($('#fileupload .files'))
.fadeIn(function () {
// Fix for IE7 and lower:
$(this).show();
});
});
// Open download dialogs via iframes,
// to prevent aborting current uploads:
$('#fileupload .files a:not([target^=_blank])').live('click', function (e) {
e.preventDefault();
$('<iframe style="display:none;"></iframe>')
.prop('src', this.href)
.appendTo('body');
});
});
#app/controllers/uploads_controller.rb
def create
#upload = Upload.new(params[:upload])
if #upload.save
render :json => [{
:pic_path => #upload.attachment.url.to_s ,
:name => #upload.attachment.instance.attributes["picture_file_name"]
}], :content_type => 'text/html'
else
render [:json => { :result => 'error'}], :content_type => 'text/html'
end
end
#app/views/uploads/new.html.haml
%link#theme{:href => "http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.13/themes/base/jquery-ui.css", :rel => "stylesheet"}
= stylesheet_link_tag 'jquery.fileupload-ui'
#fileupload
= form_for Upload.new, :html => { :multipart => true } do |f|
.fileupload-buttonbar
%label.fileinput-button
%span Add files...
= f.file_field :attachment, :multiple => true
%button.start{:type => "submit"} Start upload
%button.cancel{:type => "reset"} Cancel upload
%button.delete{:type => "button"} Delete files
.fileupload-content
%table.files
.fileupload-progressbar
%script#template-upload{:type => "text/x-jquery-tmpl"}
%tr{:class => "template-upload{{if error}} ui-state-error{{/if}}"}
%td.preview
%td.name ${name}
%td.size ${sizef}
{{if error}}
%td.error{:colspan => "2"}
Error:
{{if error === 'custom_failure'}}Custom Error Message
{{else}}${error}
{{/if}}
{{else}}
%td.progress
%div
%td.start
%button Start
{{/if}}
%td.cancel
%button Cancel
%script#template-download{:type => "text/x-jquery-tmpl"}
%tr{:class => "template-download{{if error}} ui-state-error{{/if}}"}
{{if error}}
%td
%td.name ${name}
%td.size ${sizef}
%td.error{:colspan => "2"}
Error:
{{if error === 1}}File exceeds upload_max_filesize (php.ini directive)
{{else}}${error}
{{/if}}
{{else}}
%td.preview
{{if thumbnail_url}}
%a{:href => "${url}", :target => "_blank"}
%img{:src => "${thumbnail_url}"}/
{{/if}}
%td.name
<a href="${url}"{{if thumbnail_url}} target="_blank"{{/if}}>${name}
%td.size ${sizef}
%td{:colspan => "2"}
{{/if}}
%td.delete
%button{"data-type" => "${delete_type}", "data-url" => "${delete_url}"} Delete
Edit
Had a quick look at your app, the problem is that you are mixing prototype with jquery. The easiest way around this is to switch to jQuery using jquery-rails.
#Gemfile
gem 'jquery-rails'
Next, run bundle install and then rails g jquery:install.
Then change your app/views/layouts/application.erb to this:
<%= stylesheet_link_tag :all %>
<%= csrf_meta_tag %>
<%= javascript_include_tag 'jquery.min' %>
<%= javascript_include_tag 'jquery-ui-1.8.13.custom.min' %>
<%= javascript_include_tag 'jquery.tmpl.min' %>
<%= javascript_include_tag 'jquery.iframe-transport' %>
<%= javascript_include_tag 'jquery.fileupload' %>
<%= javascript_include_tag 'jquery.fileupload-ui' %>
<%= javascript_include_tag 'jquery_ujs' %>
<%= javascript_include_tag 'application' %>
Note that I removed the
<%= javascript_include_tag :defaults %>
So that I can specify the order in which jquery, jquery_ujs, and application are loaded.
I've begun with a very similar task recently, and the swf plugin (at least the more recent one) will indeed let you update paperclip's record. It has callbacks for just about everything you'd need to extend.
:onUploadComplete (upload_options,event)
Here's Nathan Colgate's gist on the matter. He just makes a remote call to the rails server once the upload is finished telling it of the locations for the paperclip attachment.
from his uploadCompleteHandler
var uploadCompleteHandler = function(upload_options,event){
$.ajax({
url: '<%= notify_rails_of_successful_upload_path(:format => :js)%>',
global: false,
type: 'POST',
data: ({
'authenticity_token' : '<%= form_authenticity_token %>',
'upload' : {
'file_file_name' : upload_options.FileName,
'file_file_size' : upload_options.FileSize,
'file_content_type' : upload_options.ContentType
}
}),
dataType: 'script'
}
)
};
I'm not sure if this exact callback gets triggered for each file; it definitely looks like it would. But he passes everything paperclip needs back through an ajax request. filename,size,content-type. This way all that gets sent to heroku is some text about the file, sparing your app a good amount of work by giving it to the client.
edit: flash is the only way I've found to avoid sending a lot of data through heroku to s3. There are a few html5/js-only uploaders that might be able to get the job done, but the ones I have found are still pretty ripe on the dev tree.
As per Heroku support, see this.
Paperclip & multiple files upload, although not S3 specific.
View: (notice the array blog_post[avatars][])
<form accept-charset="UTF-8" action="/blog_posts" enctype="multipart/form-data" id="new_blog_post" method="post">
<div style="margin:0;padding:0;display:inline">
<input name="utf8" type="hidden" value="✓" />
<input name="authenticity_token" type="hidden" value="<%=form_authenticity_token %>" />
</div>
<p><input id="blog_post" name="blog_post[avatars][]" type="file" multiple /></p>
<p><input name="commit" type="submit" value="Upload" /></p>
</form>
Controller:
# POST /blog_posts
# POST /blog_posts.json
def create
#blog_post = BlogPost.new(params[:blog_post])
#blog_post.avatars.each do |avatar|
each_blog_post = BlogPost.new
each_blog_post.avatar = avatar
if each_blog_post.save
end
end
end
Model:
class BlogPost < ActiveRecord::Base
attr_accessible :title, :avatar, :avatars
has_attached_file :avatar
attr_accessor :avatars
end

Resources