When i add files to the queue i generate a text input for each file.
Is it possible to send this input's value to the server side?
I tried adding the value to the params object like this:
up._options.request.params.description = id;
but it only sends the id of the last image.
var up = new qq.FineUploaderBasic({
button: document.getElementById("addFiles"),
request: {
params: {
action: "upload",
},
endpoint: config.uploadURL,
},
validation: {
allowedExtensions: ["jpg", "jpeg", "png", "gif", "mkv", "mp4"],
},
autoUpload: false,
callbacks: {
onSubmit: function(id, fileName, response) {
var element = document.createElement("li");
element.setAttribute("id", "qImage-"+id);
element.setAttribute("class", "qImage");
$(".imagesList").prepend(element);
var out = "<div class='fileInfo'>";
out += "<span id='qCancel-"+id+"'>Cancel </span>";
out += fileName;
out += "</div>";
out += "<div class='fileData'>";
out += "<label for='fileDescription'>Description</label>";
out += "<input type='text' name='fileDescription' id='fileDescription'>";
out += "</div>";
$("#qImage-"+id).html(out);
$("#qCancel-"+id).on("click", function(){
up.cancel(id);
$("#qImage-"+id).remove();
});
up._options.request.params.description = id;
},
}
});
$('#startUpload').on("click", function(){
up.uploadStoredFiles();
//document.getElementById('queue-list').innerHTML = '';
});
$('#cancelUpload').on("click", function(){
up.cancelAll();
});
To expand on Ray's comment, there are two methods to my knowledge. The first may be exclusive to the S3 implementation, but I use:
uploadSuccess: {
endpoint: "link",
params: {
key: value
}
}
Or utilize setParams if you want it sent along with the file upload request itself.
Related
ok so i am creating a web app and i have run into some issues
first i make a request to an api endpoint this returns a json response i take what i need and ad it into a key value array
i then have to loop through all the items in this array and for each item i need to make a request to a second api endpoint that returns some html
i need to then append this html to an eliment on the page
i need this to be done one after another the issue i have is that the .each() loop finishes instantly while the requests are still on going in the background meaning the aliments are appended to the html eliment on the page way after they should
how can i make the loop wait untill the requests are done and html is appended before moving onto the next item in the array
$("#middlebox").fadeTo(3000, 0, function() {
$("#middlebox").html(LOADING);
});
$("#middlebox").fadeTo(3000, 1, function() {
var API_URL = window.location.href + 'api.php?action=channels&category='+$this.data("text")+'&username='+$("#username").val()+'&password='+$("#password").val();
var CHANNELS = {};
var API_CHANNELS = '<div class="Live">';
$.getJSON(API_URL).done( function( API_RESULT ) {
$.each( API_RESULT.items, function( API_INDEX, API_ITEM ) {
var API_ID = API_ITEM.stream_id;
var API_ICON = API_ITEM.stream_icon;
CHANNELS[API_ID] = API_ICON;
});
}).then( function() {
$.each( CHANNELS, function( CHANNEL_KEY, CHANNEL_VALUE ) {
var EPG_URL = window.location.href + 'api.php?action=epg&id='+CHANNEL_KEY+'&username='+$("#username").val()+'&password='+$("#password").val();
API_CHANNELS += '<div class="channel focusable"><div class="LiveIcon"><img src="' + CHANNEL_VALUE + '" class="TvIcon"></div>';
$.ajax({
url:EPG_URL,
type: 'GET',
dataType: 'html',
success:function(content,code) {
API_CHANNELS += content;
}
});
API_CHANNELS += '</div>';
});
$("#middlebox").fadeTo(3000, 0, function() {
API_CHANNELS += '</div>';
$("#middlebox").html(API_CHANNELS);
$("#middlebox").fadeTo(3000, 1, function() {
});
});
});
});
Ajax calls are asynchronous so you can't use a synchronous loop to process the requests.
You can use Promise.all to wait for all ajax requests and then process replies in a loop.
Promise.all(CHANNELS.map(function( CHANNEL_KEY, CHANNEL_VALUE ) {
var EPG_URL = window.location.href + 'api.php?action=epg&id='+CHANNEL_KEY+'&username='+$("#username").val()+'&password='+$("#password").val();
return $.ajax({
URL: EPG_URL,
type: 'GET',
dataType: 'html'
}).then(function(content) {
return [CHANNEL_KEY, CHANNEL_VALUE, content];
});
})).then(function(channels) {
$.each(channels, function(CHANNEL_KEY, CHANNEL_VALUE, content) {
API_CHANNELS += '<div class="channel focusable"><div class="LiveIcon"><img src="' + CHANNEL_VALUE + '" class="TvIcon"></div>';
API_CHANNELS += content;
API_CHANNELS += '</div>';
});
$("#middlebox").fadeTo(3000, 0, function() {
/* ... */
});
});
i'm creating a datatable for insert, update. edit event will fire on the click of but it is not firing any event on click, even the debug is not pointing toward it on click.
function GetLoginData() {
$.ajax({
url: '/LoginMaster/GetLoginData',
type: 'get',
dataType: 'json',
success: function (result) {
var GetLoginData = result;
//$('#tblLoginMaster').DataTable.clear();
//$('#tblLoginMaster').DataTable.destroy();
// table.empty();
if (GetLoginData != '') {
var str = "";
$.each(GetLoginData, function (index, obj) {
var GetID = obj.Id == null ? "" : obj.Id;
var GetName = obj.Name == null ? "" : obj.Name;
var GetEmailID = obj.Email == null ? "" : obj.Email;
var GetPassword = obj.Password == null ? "" : obj.Password;
var GetRole = obj.Role == null ? "" : obj.Role;
This is where my editstyle class is
str += "<tr class='EditStyle' data-target='#addUserModal' data-toggle='modal' value='" + GetID +"'>";
str += "<td>" + GetID + "</td>";
str += "<td>" + GetName + "</td>";
str += "<td>" + GetEmailID + "</td>";
str += "<td>" + GetPassword + "</td>";
str += "<td>" + GetRole + "</td>";
str += "</tr>"
$('#tblbodyLoginMaster').html(str);
});
$('#tblLoginMaster').DataTable({
"paging": true,
"lengthChange": false,
"searching": false,
"ordering": true,
"info": true,
"autoWidth": false,
"sDom": 'lfrtip'
});
}
}
});
}
This is my click event
$('tr.EditStyle').on('click', function () {
if ((this).val() != 0) {
var rowData = (this).children('td').val();
$('#id').val(rowData[0]);
$('#username').val(rowData[1]);
$('#emailId').val(rowData[2]);
$('#password').val(rowData[3]);
$('#role').val(rowData[4]);
}
});
I'm suspecting your click handler doesn't work as your rows do not exist by the time you attempt to listen for clicks. Try to change that, like this:
$('#tblLoginMaster').on('click', 'tr.EditStyle', function () {
...
});
If that solves your problem, you may check out oficial tutorial on that topic.
Another thing in your code that raises my concern is using jQuery $.ajax() method to populate your table and the very method you populate that:
in order to fetch your data dynamically, you should use DataTables ajax option and take the advantage of ajax.dataSrc:
$('#tblLoginMaster').DataTable({
...,
ajax: {
url: '/LoginMaster/GetLoginData',
method: 'GET',
dataSrc: //here goes json property or the method that retrieves table data out of json response
}
});
you don't need to cook up table body HTML on your own, you may simply point to corresponding source data properties within columns/columnDefs options, and make use of createdRow option to assign necessary row attributes:
$('#tblLoginMaster').DataTable({
...,
rowCreated: (tr, _, rowIdx) => $(tr).attr({
'class':'EditStyle',
'data-target': '#addUserModal',
'data-toggle': 'modal',
'value': rowIdx
}),
columns: [
{data: 'Id', header: 'Id'},
{data: 'Name', header: 'Name'},
{data: 'Password', header: 'Password'},
...
]
});
You may be reluctant to do such a global change to your app, but otherwise you may run into different sorts of issues related to AJAX-async nature and deprive yourself of the possibility to use the plethora of DataTables API methods to catch the bugs or enrich your app with new features.
I have a hidden form created with a jquery plugin and I need to run a function after the submit has happened but it doesn't appear to be getting called.
I need to get the new csrf details after the form has been posted.
After submitting the form I want to get the newly generated csrf details
$(this).on('click', function() {
$('#' + settings.title.replace(/\s+/g, '-').toLowerCase() + '-export-csv').submit();
get_csrf_details();
});
Html link with export-csv class and data which will be used in the plugin. Using smarty template.
<a href="#" class="export-csv" data-title='Landlords' data-data='{base64_encode($landlords_json)}'>
Export (CSV)
</a>
ExportCSV plugin
(function($) {
$.fn.exportCSV = function ( options ) {
return $(this).each(function() {
var settings = $.extend({
title: null,
data: null,
link: '/',
}, options);
settings.title = $(this).data('title');
settings.data = $(this).data('data');
var hidden_form = "<form id='" + settings.title.replace(/\s+/g, '-').toLowerCase() + "-export-csv' action='" + settings.link + "' method='POST' style='display: none;'>" +
"<input type='hidden' class='csrf_field' name='" + csrfName + "' value='" + csrfHash + "'>" +
"<input type='hidden' name='title' value='" + settings.title + "'>" +
"<input type='hidden' name='data' value='" + settings.data + "'>" +
"</form>";
$(this).append(hidden_form);
$(this).on('click', function() {
$('#' + settings.title.replace(/\s+/g, '-').toLowerCase() + '-export-csv').submit();
get_csrf_details();
});
});
}
}(jQuery));
$(".export-csv").exportCSV({
link: '/dashboard/export-csv'
});
// get the csrf details from server
var get_csrf_details = function get_csrf_details() {
$.get('/ajax/get-csrf-details', function(response) {
var csrfName = response.data.csrf.name;
var csrfHash = response.data.csrf.hash;
// const csrf_input1 = document.querySelector('.csrf_field');
const csrf_inputs = document.getElementsByClassName('csrf_field');
for (i = 0; i < csrf_inputs.length; i++) {
csrf_inputs[i].name = csrfName;
csrf_inputs[i].value = csrfHash;
}
});
};
There's no way to know when a submission from a <form> element has been successfully completed.
However, given what you're doing it would make much more sense to just use AJAX. This means you can control the exact logic executed when a response is received and saves having to inject a hidden form and faking a submission, which is far from ideal. Try this:
$.fn.exportCSV = function(options) {
return $(this).each(function() {
var settings = $.extend({
title: null,
data: null,
link: '/',
}, options);
settings.title = $(this).data('title');
settings.data = $(this).data('data');
$(this).on('click', function() {
var data = {
title: settings.title,
data: settings.data
};
data[csrfName] = csrfHash;
$.ajax({
url: settings.link,
type: 'POST',
data: data,
success: function(response) {
// the submission has been made, perform required logic here.
get_csrf_details();
},
error: function() {
// something went wrong, debug it!
}
});
});
});
}
A couple of things to note. Firstly, it may make more sense to return the new CSRF in the response of the first request. This will save your network traffic.
Secondly, you're always setting settings.title and settings.data to match the data attributes on the element this function was defined on, so using a settings object is pointless as it will always be overwritten, even if no data attributes are provided. You could instead amend the logic to only use the data if they exist.
I have a Horse model and a Photo model. I am using jQuery File Upload to resize (client side) images and save on Amazon s3 directly since I am using Heroku.
I have seen other questions similar that use carrierwave, paperclip, or that are very old. I am not sure why you would use carrierwave/paperclip, but I think based on what heroku says, I do not want to have images hitting the server potentially causing time-outs.
Heroku recommends using jQuery File Upload and shows js appending new file input with a value of the image's link (returned from amazon s3). I have this working when saving a photo separately. I now want to make it work in a nested form for Horse but js is not finding input (since it does not exist yet because it's nested I presume).
I am using Cocoon for nested forms (I am open to anything that will work better). I am not too familiar with javascript/jQuery but a far as I can tell, Cocoon 'hides' the nested element until I click to add it via the add_association.
haml view code:
= link_to_add_association 'add photo', f, :photos
html source before clicking 'add photo'
<div class='links'>
<a class="btn btn-default btn-sm add_fields" data-association-insertion-method="after" data-association="photo" data-associations="photos" data-association-insertion-template="<div class='nested-fields'>
<fieldset class="inputs"><ol><input type="file" name="horse[photos_attributes][new_photos][url]" id="horse_photos_attributes_new_photos_url" />
<input type="hidden" name="horse[photos_attributes][new_photos][_destroy]" id="horse_photos_attributes_new_photos__destroy" value="false" /><a class="remove_fields dynamic" href="#">remove photo</a>
</ol></fieldset>
</div>
" href="#">add photo</a>
How do I work with this input and how do I handle multiple file uploads as they are added correctly?
My current upload js:
$(function() {
if ($('#new_horse').length > 0) {
$.get( "/presign", function( s3params ) {
$('.direct-upload').find("input:file").each(function(i, elem) {
var fileInput = $(elem);
var form = $(fileInput.parents('form:first'));
var submitButton = form.find('input[type="submit"]');
var progressBar = $("<div class='bar'></div>");
var barContainer = $("<div class='progress'></div>").append(progressBar);
fileInput.fileupload({
fileInput: fileInput,
url: "http://" + s3params.url.host,
type: 'POST',
autoUpload: true,
formData: s3params.fields,
paramName: 'file', // S3 does not like nested name fields i.e. name="user[avatar_url]"
dataType: 'XML', // S3 returns XML if success_action_status is set to 201
disableImageResize: false,
imageQuality: 0.5,
disableImageResize: /Android(?!.*Chrome)|Opera/
.test(window.navigator && navigator.userAgent),
imageMaxWidth: 500,
imageMaxHeight: 1000,
imageOrientation: true, //auto rotates images
acceptFileTypes: /(\.|\/)(gif|jpe?g|png)$/i, //I added this to jquery.fileupload-validate: alert('Must Be JPG GIF or PNG Image')
replaceFileInput: false,
progressall: function (e, data) {
var progress = parseInt(data.loaded / data.total * 100, 10);
progressBar.css('width', progress + '%')
},
start: function (e) {
submitButton.prop('disabled', true);
fileInput.after(barContainer);
progressBar.
css('background', 'green').
css('display', 'block').
css('width', '0%').
text("Loading...");
},
done: function(e, data) {
submitButton.prop('disabled', false);
progressBar.text("Pre-uploading done... Please Save or Cancel");
// extract key and generate URL from response
var key = $(data.jqXHR.responseXML).find("Key").text();
var url = 'https://' + s3params.url.host +'/' + key;
// remove first input to prevent phantom upload delay
fileInput.remove();
// create new hidden input with image url
var input = $("<input />", { type:'hidden', name: fileInput.attr('name'), value: url })
var imgPreview = '<img src="' + url + '">';
form.append(input);
form.append(imgPreview);
},
fail: function(e, data) {
submitButton.prop('disabled', false);
progressBar.
css("background", "red").
text("Failed");
}
});
});
}, 'json');
}
});
I guess I should have looked at cocoon documentation first:
http://www.rubydoc.info/gems/cocoon#Callbacks__upon_insert_and_remove_of_items_
http://www.rubydoc.info/gems/cocoon/1.2.6
I modified my upload.js file to the following and it worked for multiple files in nested forms perfectly:
// added for file uploading
// https://devcenter.heroku.com/articles/direct-to-s3-image-uploads-in-rails
// Get our s3params from our endpoint
$(document).on('ready page:load', function () {
$('.direct-upload')
.on('cocoon:after-insert', function(e, photo) {
console.log('inside cocoon image function');
$.get( "/presign", function( s3params ) {
$('.direct-upload').find("input:file").each(function(i, elem) {
console.log('inside nested-fields photo input form');
var fileInput = $(elem);
var form = $(fileInput.parents('form:first'));
var submitButton = form.find('input[type="submit"]');
var progressBar = $("<div class='bar'></div>");
var barContainer = $("<div class='progress'></div>").append(progressBar);
fileInput.fileupload({
fileInput: fileInput,
url: "http://" + s3params.url.host,
type: 'POST',
autoUpload: true,
formData: s3params.fields,
paramName: 'file', // S3 does not like nested name fields i.e. name="user[avatar_url]"
dataType: 'XML', // S3 returns XML if success_action_status is set to 201
disableImageResize: false,
imageQuality: 0.5,
disableImageResize: /Android(?!.*Chrome)|Opera/
.test(window.navigator && navigator.userAgent),
imageMaxWidth: 500,
imageMaxHeight: 1000,
imageOrientation: true, //auto rotates images
acceptFileTypes: /(\.|\/)(gif|jpe?g|png)$/i, //I added this to jquery.fileupload-validate: alert('Must Be JPG GIF or PNG Image')
replaceFileInput: false,
previewMaxWidth: 100,
previewMaxHeight: 100,
previewCrop: true,
progressall: function (e, data) {
var progress = parseInt(data.loaded / data.total * 100, 10);
progressBar.css('width', progress + '%')
},
start: function (e) {
submitButton.prop('disabled', true);
fileInput.after(barContainer);
progressBar.
css('background', 'green').
css('display', 'block').
css('width', '0%').
text("Loading...");
},
done: function(e, data) {
submitButton.prop('disabled', false);
progressBar.text("Photo Uploaded");
// extract key and generate URL from response
var key = $(data.jqXHR.responseXML).find("Key").text();
var url = 'https://' + s3params.url.host +'/' + key;
// remove first input to prevent phantom upload delay
fileInput.remove();
// create new hidden input with image url
var input = $("<input />", { type:'hidden', name: fileInput.attr('name'), value: url })
var imgPreview = '<img src="' + url + '">';
form.append(input);
form.append(imgPreview);
},
fail: function(e, data) {
submitButton.prop('disabled', false);
progressBar.
css("background", "red").
text("Failed");
}
}, 'json'); //fileupload
}); //each file
}); //presign call
}); // function cocoon
}); // page ready
I guess Google and a well documented gem can replace knowledge of JS in the short term :-) I am sure it's not at tight as it could be so please offer any improvements.
How can I get data response after uploading image and insert that response into a tag as data-id attribute (after successful post I am getting id of inserted image ). Where in the function is happening this? The function:
function img_upload(url) {
{
var fileTemplate = "<div id=\"{{id}}\">";
fileTemplate += "<div class=\"preview\"></div>";
fileTemplate += "<div class=\"filename\">{{filename}}</div>";
fileTemplate += "ObriĊĦi Sliku";
fileTemplate += "</div>";
function slugify(text) {
text = text.replace(/[^-a-zA-Z0-9,&\s]+/ig, '');
text = text.replace(/-/gi, "_");
text = text.replace(/\s/gi, "-");
return text;
}
$("#dropbox").html5Uploader({
postUrl: url,
onClientLoadStart: function (e, file, data) {
var upload = $("#upload");
if (upload.is(":hidden")) {
upload.show();
}
upload.append(fileTemplate.replace(/{{id}}/g, slugify(file.name)).replace(/{{filename}}/g, file.name));
console.log(data);
},
onClientLoad: function (e, file) {
$("#" + slugify(file.name))
.find(".preview")
.append("<img class=img_upload title=\"" + file.name + "\" src=\"" + e.target.result + "\" alt=\"\">")
.on('click', function () {
img_name = $(this).find('.img_upload').attr('title'),
url = '<?php echo base_url() ?>admin/galerija_naslovna_slika/' + img_name.replace(/\s/g, "_") + '/' + id;
$.post(url);
});
var img_delete = $('.image_delete');
delete_image(img_delete);
},
onServerLoad: function (e, file) {
}
});
}
}
I know this is old, But what I do is pass the id in the postURL.
So something like: /image_upload/212/
You need to use apache's mod_rewrite though to get to your php file. And then you can simply look at the request path to get the ID.
Or the other way to do it is pass the ID as a POST var.
You could have a hidden "input" with the id as the value.
docid = $('#docid').val();
$.ajax({
url: '/upload.php',
type: "POST",
data: {'docid': docid},
success: function(data, status, jqXHR) {
set_status('info', 'Success!');
},
error: function(jqXHR, status, err) {
set_status('danger', 'Error: ' + err + ' - ' + status);
},
complete: function(jqXHR, status) {
}
});