Knockout JS: Fileupload event - javascript

I have this knockout js script for uploading file
This code triggers the upload event when the user selects a file in the upload control
Upload.html
$(function() {
var viewModel = {
filename: ko.observable(""),
};
ko.applyBindings(viewModel);
});
<form>
<input id="upload" name="upload"
data-bind="fileUpload: { property: 'filename', url: 'http://localhost/api/upload/PostFormData' }"
type="file" />
<button id="submitUpload">Upload</button>
</form>
FileUpload.js
ko.bindingHandlers.fileUpload = {
init: function (element, valueAccessor) {
$(element).after('<div class="progress"><div class="bar"></div><div class="percent">0%</div></div><div class="progressError"></div>');
},
update: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
var options = ko.utils.unwrapObservable(valueAccessor()),
property = ko.utils.unwrapObservable(options.property),
url = ko.utils.unwrapObservable(options.url);
if (property && url) {
$(element).change(function() {
if (element.files.length) {
var $this = $(this),
fileName = $this.val();
// this uses jquery.form.js plugin
$(element.form).ajaxSubmit({
url: url,
type: "POST",
dataType: "text",
headers: { "Content-Disposition": "attachment; filename=" + fileName },
beforeSubmit: function() {
$(".progress").show();
$(".progressError").hide();
$(".bar").width("0%")
$(".percent").html("0%");
},
uploadProgress: function(event, position, total, percentComplete) {
var percentVal = percentComplete + "%";
$(".bar").width(percentVal)
$(".percent").html(percentVal);
},
success: function(data) {
//$(".progress").hide();
//$(".progressError").hide();
// set viewModel property to filename
$("label[for='upload']").text(data);
bindingContext.$data[property](data);
},
error: function(jqXHR, errorThrown) {
$(".progress").hide();
$("div.progressError").html(jqXHR.responseText);
}
});
}
});
}
}
}
Now, I want to move the triggering of upload event to the submit button
<button id="submitUpload">Upload</button>
How to do this? Right now this is where I'm at, I just move the upload event inside the click event of the button. But it's not working, and it doesn't call the ajax request to the API.
$('#submitUpload').click(function () {
if (element.files.length) {
var $this = $(element),
fileName = $this.val();
//alert(element.form);
// this uses jquery.form.js plugin
$(element.form).ajaxSubmit({
url: url,
type: "POST",
dataType: "text",
headers: { "Content-Disposition": "attachment; filename=" + fileName },
beforeSubmit: function() {
$(".progress").show();
$(".progressError").hide();
$(".bar").width("0%")
$(".percent").html("0%");
},
uploadProgress: function(event, position, total, percentComplete) {
var percentVal = percentComplete + "%";
$(".bar").width(percentVal)
$(".percent").html(percentVal);
},
success: function(data) {
//$(".progress").hide();
//$(".progressError").hide();
// set viewModel property to filename
$("label[for='upload']").text(data);
bindingContext.$data[property](data);
},
error: function(jqXHR, errorThrown) {
$(".progress").hide();
$("div.progressError").html(jqXHR.responseText);
}
});
}
});

Instead of passing only name, URL to the bindinghandler pass third parameter (fileBinaryData) from your ViewModel Object then read the file Content in KO BindingHandler's Update method then update third observable (fileBinaryData) in update method.
Then you can use this filebinary data in you viewmodel
so for the button bind click event and access fileBinaryData observable which will have the file content.
BindingHandler:
ko.bindingHandlers.FileUpload = {
init: function (element, valueAccessor) {
$(element).change(function () {
var file = this.files[0];
if (ko.isObservable(valueAccessor())) {
valueAccessor()(file);
}
});
},
update: function (element, valueAccessor, allBindingsAccessor) {
var file = ko.utils.unwrapObservable(valueAccessor());
var bindings = allBindingsAccessor();
if (bindings.fileBinaryData && ko.isObservable(bindings.fileBinaryData)) {
if (!file) {
bindings.fileBinaryData(null);
} else {
var reader = new window.FileReader();
reader.onload = function (e) {
bindings.fileBinaryData(e.target.result);
};
reader.readAsBinaryString(file);
}
}
}
}
HTML:
<input type="file" id="fileUpload" class="file_input_hidden" data-bind="FileUpload: spFile, fileObjectURL: spFileObjectURL, fileBinaryData: spFileBinary" />
ViewModel:
var viewModel = {
filename: ko.observable(""),
url: ko.observable(),
spFileBinary:ko.observable(),
//Write your CLICK EVENTS
};
Hope This Helps :)

element is unknown at the moment of click. you need to find it on the form.
Start the first line of your click function with
element = $('#upload').get(0);
and replace your button tag with the following
<input type="button" id="submitUpload" value="Upload"></input>
because the button tag automatically submits the form.

Related

Bootstrap modal js is giving Synchronous XMLHttpRequest on the main thread is deprecated message

I am using Bootstrap Modal Js. However, whenever the modal pops up , I am getting this error
Synchronous XMLHttpRequest on the main thread is deprecated because of its detrimental effects to the end user's experience. For more help, check https://xhr.spec.whatwg.org/.
Also, in one page I am getting the erorr below when the modal launches using
if (auth=="False"){
setTimeout(function(){
if ($('#registerModal').hasClass('show')){
}else{
$('.register-btn').trigger('click');
}
}, 25000);
}
Error:
jquery.validate.unobtrusive.min.js:5 Uncaught TypeError: Cannot set property 'unobtrusive' of undefined
at jquery.validate.unobtrusive.min.js:5
at jquery.validate.unobtrusive.min.js:5
Here is the js
/*
django-bootstrap-modal-forms
version : 2.0.1
Copyright (c) 2020 Uros Trstenjak
https://github.com/trco/django-bootstrap-modal-forms
*/
(function ($) {
// Open modal & load the form at formURL to the modalContent element
var modalForm = function (settings) {
$(settings.modalID).find(settings.modalContent).load(settings.formURL, function () {
settings.asyncSettings=true;
settings.async = true;
$(settings.modalID).modal("show");
$(settings.modalForm).attr("action", settings.formURL);
addEventHandlers(settings);
});
};
var addEventHandlers = function (settings) {
// submitBtn click handler
$(settings.submitBtn).on("click", function (event) {
isFormValid(settings, submitForm);
});
// Modal close handler
$(settings.modalID).on("hidden.bs.modal", function (event) {
$(settings.modalForm).remove();
});
};
// Check if form.is_valid() & either show errors or submit it via callback
var isFormValid = function (settings, callback) {
$.ajax({
type: $(settings.modalForm).attr("method"),
url: $(settings.modalForm).attr("action"),
data: new FormData($(settings.modalForm)[0]),
contentType: false,
processData: false,
beforeSend: function () {
$(settings.submitBtn).prop("disabled", true);
},
success: function (response) {
if ($(response).find(settings.errorClass).length > 0) {
// Form is not valid, update it with errors
$(settings.modalID).find(settings.modalContent).html(response);
$(settings.modalForm).attr("action", settings.formURL);
// Reinstantiate handlers
addEventHandlers(settings);
} else {
// Form is valid, submit it
callback(settings);
}
}
});
};
// Submit form callback function
var submitForm = function (settings) {
if (!settings.asyncUpdate) {
$(settings.modalForm).submit();
} else {
var asyncSettingsValid = validateAsyncSettings(settings.asyncSettings);
var asyncSettings = settings.asyncSettings;
if (asyncSettingsValid) {
var formdata = new FormData($(settings.modalForm)[0]);
// Add asyncUpdate and check for it in save method of CreateUpdateAjaxMixin
formdata.append("asyncUpdate", "True");
$.ajax({
type: $(settings.modalForm).attr("method"),
url: $(settings.modalForm).attr("action"),
data: formdata,
contentType: false,
processData: false,
success: function (response) {
var body = $("body");
if (body.length === 0) {
console.error("django-bootstrap-modal-forms: <body> element missing in your html.");
}
body.prepend(asyncSettings.successMessage);
// Update page without refresh
$.ajax({
type: "GET",
url: asyncSettings.dataUrl,
dataType: "json",
success: function (response) {
// Update page
$(asyncSettings.dataElementId).html(response[asyncSettings.dataKey]);
// Add modalForm to trigger element after async page update
if (asyncSettings.addModalFormFunction) {
asyncSettings.addModalFormFunction();
}
if (asyncSettings.closeOnSubmit) {
$(settings.modalID).modal("hide");
} else {
// Reload form
$(settings.modalID).find(settings.modalContent).load(settings.formURL, function () {
$(settings.modalForm).attr("action", settings.formURL);
addEventHandlers(settings);
});
}
}
});
}
});
}
}
};
var validateAsyncSettings = function (settings) {
var missingSettings = [];
if (!settings.successMessage) {
missingSettings.push("successMessage");
console.error("django-bootstrap-modal-forms: 'successMessage' in asyncSettings is missing.");
}
if (!settings.dataUrl) {
missingSettings.push("dataUrl");
console.error("django-bootstrap-modal-forms: 'dataUrl' in asyncSettings is missing.");
}
if (!settings.dataElementId) {
missingSettings.push("dataElementId");
console.error("django-bootstrap-modal-forms: 'dataElementId' in asyncSettings is missing.");
}
if (!settings.dataKey) {
missingSettings.push("dataKey");
console.error("django-bootstrap-modal-forms: 'dataKey' in asyncSettings is missing.");
}
if (!settings.addModalFormFunction) {
missingSettings.push("addModalFormFunction");
console.error("django-bootstrap-modal-forms: 'addModalFormFunction' in asyncSettings is missing.");
}
if (missingSettings.length > 0) {
return false;
}
return true;
};
$.fn.modalForm = function (options) {
// Default settings
var defaults = {
modalID: "#modal",
modalContent: ".modal-content",
modalForm: ".modal-content form",
formURL: null,
errorClass: ".invalid",
submitBtn: ".submit-btn",
asyncUpdate: true,
asyncSettings: {
closeOnSubmit: false,
successMessage: null,
dataUrl: null,
dataElementId: null,
dataKey: null,
addModalFormFunction: null
}
};
// Extend default settings with provided options
var settings = $.extend(defaults, options);
this.each(function () {
// Add click event handler to the element with attached modalForm
$(this).click(function (event) {
// Instantiate new form in modal
modalForm(settings);
});
});
return this;
};
}(jQuery));

AjaxSubmit submits form twice (or three times or 20 times)

I'm new here and new to JS :-)
I'm using AJAX SUBMIT (jquery) to send files to my CDN.
the first time - everything is ok, the file is sent and received.
second time: the file is sent and received, only it does that twice.
and the third time its three times etc...
that's the console of the second time: https://prnt.sc/tluygu
as you can see, it submits twice.
That's the code of the submission: (it's in a modal)
$(document).on('click', '.add-video-btn', function() {
$('#file').val('');
$('#file-upload').trigger('reset');
$('#video-uploading-modal').modal({
backdrop: 'static',
keyboard: false
});
$('#upload-btn').on('click', async function() {
var vidRes = await getOneTimeUploadUrl();
console.log(vidRes.result.uploadURL);
var oneTimeUrl = vidRes.result.uploadURL;
$('#file-upload').ajaxSubmit({
url: oneTimeUrl,
beforeSubmit: function(formData, formObject, formOptions) {
console.log(formData);
$('.progress').slideDown();
},
beforeSend: function() {},
uploadProgress: function(event, position, total, precentComplete) {
$('.progress-bar').css('width', precentComplete + '%');
$('.progress-bar').html('%' + precentComplete);
},
success: function(response) {
console.log(response);
alert('success');
},
});
});
});
The problem is that every time the modal is opened, a new submit listener gets added. To fix your problem, you need to split the event handlers:
Start with the modal click listener:
$(document).on('click', '.add-video-btn', function() {
$('#file').val('');
$('#file-upload').trigger('reset');
$('#video-uploading-modal').modal({
backdrop: 'static',
keyboard: false
});
});
Then, set the submit listener sepparately:
$('#upload-btn').on('click', async function() {
var vidRes = await getOneTimeUploadUrl();
console.log(vidRes.result.uploadURL);
var oneTimeUrl = vidRes.result.uploadURL;
$('#file-upload').ajaxSubmit({
url: oneTimeUrl,
beforeSubmit : function(formData, formObject, formOptions) {
console.log(formData);
$('.progress').slideDown();
},
beforeSend : function() {
},
uploadProgress : function(event, position, total, precentComplete) {
$('.progress-bar').css('width', precentComplete + '%');
$('.progress-bar').html('%' + precentComplete);
},
success: function(response) {
console.log(response);
alert('success');
},
});
});
That should solve your issue. Hope you've found this helpful.

BackBone Views Events Hash

I am new to backbone and am doing a project to teach myself. For some reason I cannot get the events hash to work so I did a good amount of things in the initialize function instead. How would I use the events hash in my view below?
var PictureView = Backbone.View.extend({
el: "#app",
initialize: function() {
$('.button').click(function() {
this.request();
}.bind(this))
},
request: function(text) {
var text = $('#text').val();
this.getPicture(text, function(url) {
console.log(arguments)
//append it to the image tag;
$("#random").attr("src", url)
});
},
getPicture: function(tags, cb) {
$.getJSON(
"https://api.flickr.com/services/rest/?jsoncallback=?", {
method: 'flickr.photos.search',
tags: tags,
api_key: apiKey,
format: 'json',
nojsoncallback: 1,
per_page: 10 // you can increase this to get a bigger array
},
function(data) {
if (data.stat === 'ok') {
var photo = data.photos.photo[Math.floor(Math.random() * data.photos.photo.length)];
$.getJSON(
"https://api.flickr.com/services/rest/?jsoncallback=?", {
method: 'flickr.photos.getSizes',
api_key: apiKey,
photo_id: photo.id,
format: 'json',
nojsoncallback: 1
},
function(response) {
console.log(response);
if (response.stat === 'ok') {
var the_url = response.sizes.size[5].source;
cb(the_url);
} else {
console.log(" The request to get the picture was not good :\ ")
}
}
);
} else {
alert("no pictures found for that tag :'(");
}
}
);
}
})
Your button is outside the div with id #app. In Backbone, for the events hash to work, the elements you want to use the events on, should be inside your el.
<center><div id="app">
<center><button class="button">Click Me to add an image</button</center>
</div></center>
Now you can use the events hash as
el: "#app",
events: { 'click .button': 'request' },
initialize : function(){}

data-bind not displaying data in view page

This is how I have my page set up, but no data is being displayed, and I'm not sure why:
JavaScript/knockout:
var getList = function () {
Ajax.Get({
Url: ...,
DataToSubmit: {id: properties.Id },
DataType: "json",
OnSuccess: function (roleData, status, jqXHR) {
bindModel(roleData);
}
});
};
// Binds the main ViewModel
var bindModel = function (data) {
var _self = viewModel;
ko.applyBindings(viewModel, $('#ListView')[0]);
};
var viewModel = {
ListRoleTypes: ko.observableArray([]),
.....
};
var roleViewModel = function (data) {
var _self = this;
_self.ContentRole = ko.observable(data.ContentRole);
_self.RoleName = ko.observable(data.RoleName);
_self.RoleRank = ko.observable(data.RoleRank);
_self.UserCount = ko.observable(data.UserCount);
};
This is my View page:
<div data-bind="foreach: ListRoleTypes">
<span data-bind="text: RoleName"></span>
</div>
Any thoughts on where I am going wrong?
you are calling bindmodel and passing in the roledata, but then in bindmodel, you dont do anything with it.
Ajax.Get({
Url: ...,
DataToSubmit: {id: properties.Id },
DataType: "json",
OnSuccess: function (roleData, status, jqXHR) {
bindModel(roleData);
}
});
};
// Binds the main ViewModel
var bindModel = function (data) {
// need to do something with viewmodel to handle the passed in data
viewmodel.initialize(data);
ko.applyBindings(viewModel, $('#ListView')[0]);
};

Web API: Upload files via Ajax post

I have this following snippet for uploading files via Ajax post (using Knockout js library)
ko.bindingHandlers.fileUpload = {
init: function (element, valueAccessor) {
$(element).after('<div class="progress"><div class="bar"></div><div class="percent">0%</div></div><div class="progressError"></div>');
},
update: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
var options = ko.utils.unwrapObservable(valueAccessor()),
property = ko.utils.unwrapObservable(options.property),
url = ko.utils.unwrapObservable(options.url);
if (property && url) {
$(element).change(function () {
if (element.files.length) {
var $this = $(this),
fileName = $this.val();
alert(fileName);
// this uses jquery.form.js plugin
$(element.form).ajaxSubmit({
url: url, //WEB API URL
type: "POST",
dataType: "text", //I want to upload .doc / excell files
headers: { "Content-Disposition": "attachment; filename=" + fileName },
beforeSubmit: function () {
$(".progress").show();
$(".progressError").hide();
$(".bar").width("0%")
$(".percent").html("0%");
},
uploadProgress: function (event, position, total, percentComplete) {
var percentVal = percentComplete + "%";
$(".bar").width(percentVal)
$(".percent").html(percentVal);
},
success: function (data) {
$(".progress").hide();
$(".progressError").hide();
// set viewModel property to filename
bindingContext.$data[property](data);
},
error: function (jqXHR, textStatus, errorThrown) {
$(".progress").hide();
$("div.progressError").html(jqXHR.responseText);
}
});
}
});
}
}
}
now my problem is creating the WEB API for this.
$(element.form).ajaxSubmit({
url: url, //WEB API URL
type: "POST",
dataType: "text", //I want to upload .doc / excell files
headers: { "Content-Disposition": "attachment; filename=" + fileName },
I want to upload Word/Excell document. Can someone give me an idea how to get this done in ASP.NET WEB API?
UPDATE:
I finally made an WEB API for this
public class UploadController : ApiController
{
public async Task<HttpResponseMessage> PostFormData()
{
// Check if the request contains multipart/form-data.
if (!Request.Content.IsMimeMultipartContent())
{
throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
}
string root = HttpContext.Current.Server.MapPath("~/App_Data");
var provider = new MultipartFormDataStreamProvider(root);
try
{
// Read the form data.
await Request.Content.ReadAsMultipartAsync(provider);
// This illustrates how to get the file names.
foreach (MultipartFileData file in provider.FileData)
{
Trace.WriteLine(file.Headers.ContentDisposition.FileName);
Trace.WriteLine("Server file path: " + file.LocalFileName);
}
return Request.CreateResponse(HttpStatusCode.OK);
//return new HttpResponseMessage()
//{
// Content = new StringContent("File uploaded.")
//};
}
catch (System.Exception e)
{
return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, e);
}
}
It works ok (data successfully uploaded) but the problem is, it seems like in doesnt hit this section after the data is succesfully uploaded.
success: function (data) {
$(".progress").hide();
$(".progressError").hide();
// set viewModel property to filename
bindingContext.$data[property](data);
},
I have to inform the client of what the status of the upload.

Categories

Resources