Since I'm comparatively new to Knockout.js, which would be the best approach in Knockout to upload all files one after the other using a single button?
Currently, the user has to click on individual Upload button to upload one or more files to an azure container. See FileModel below.
An enhancement to this feature is to allow user to upload all files at once
using Upload All Files button. See PageModel below.
What I have tried so far is to iterate through each file object in the Files = ko.observableArray() and upload it using its individual AJAX but that would defeat the purpose of using an AJAX call.
PageModel
var PageModel = function (r) {
var self = this;
this.Files = ko.observableArray();
this.UploadAllFiles = function (e) {
// Don't want to use this, because AJAX!
ko.utils.arrayForEach(self.Files(), function (file) {
file.UploadFile();
});
}
}
FileModel
var FileModel = function (r, fileObject) {
var self = this;
this.FileObject = fileObject;
this.Name = r.name;
this.Comment = ko.observable("");
this.UploadFile = function () {
var formData = new FormData();
formData.append('File', fileObject);
formData.append('Comment', self.Comment());
$.ajax({
url: '/Controller/Action',
type: 'POST',
data: formData,
processData: false,
contentType: false,
success: function (data) {
},
error: function (data) {
}
});
}
}
window.model = new PageModel();
ko.applyBindings(model);
PS: There is also a manually entered string associated with every file using data-bind="textInput : Comment". This additional information is also passed through with the file object in the AJAX call.
Related
I'm aiming to send fileform with images and additional information like height and width. I can't figure out how to add some custom props to file form object.
$("#saveImg").on('click', function () {
var formData = new FormData(),
allFiles = [];
$('input[name=fileUpload]').each(function (index) {
if (inputFileValidation(this)) {
(function (files) {
if (files.length != 0) { allFiles.push(files[0]); }
})(this.files)
}
});
for (var i = 0; i != allFiles.length; i++) {
var img = new Image()
img.src = window.URL.createObjectURL(allFiles[i]);
$(img).on('load', function () {
formData.append("files_h", img.naturalHeight);
formData.append("files_w", img.naturalWidth);
formData.append("files", allFiles[i]);
window.URL.revokeObjectURL(allFiles[i]);
});
}
$.ajax({
url: '#Url.Action("Upload", "Image")',
data: formData,
processData: false,
contentType: false,
type: "POST",
success: function () {}
errors: function () {}
});
});
[HttpPost]
public async Task<IActionResult> Upload (IList<IFormFile> files)
{
//do something ;
}
I also tried:
[HttpPost]
public async Task<IActionResult> Upload (IList<IFormFile> files, IList<IFormFile> files_h, IList<IFormFile> files_w)
{
//do something ;
}
Maybe you have another idea how to send image with additional data? I tried to convert file form and additional info to JSON by that didn't work.
Edit
Thank you all for your suggestion, they are really important to me because I will definitely use them in the future.
However, in this project I have already given up using the file reader due to its asynchrony and having fun with promise. The aim is simple and less javascript and more c#
I apologize for misleading you in the title javascript andjquery - and I mark the answer related to c #. I did this because this answer is related to my next task because the CoreCompat.System.Drawing library is undoubtedly still useful for editing photos in the future.
Thanks!!
If you want to get the Width and Height properties while uploading images in ASP.NET Core. I suggest you to install this package: CoreCompat.System.Drawing
Install-Package CoreCompat.System.Drawing -Version 1.0.0-beta006
In the server, after saving your files to the specific path. You could use System.Drawing.Image class to get the Width and Height properties:
using (var image = System.Drawing.Image.FromFile(filePath))
{
int width = image.Width;
int height = image.Height;
}
You don't have to add files_h and files_w properties to your client model before sending to server.
And then, by using this way, I've edited your js code to:
$("#saveImg").on('click', function () {
var formData = new FormData();
for (var input of Array.from($('input[name=fileUpload]')))
{
if (inputFileValidation(input) && input.files.length) {
formData.append('files', input.files[0]);
}
}
$.ajax({
url: '#Url.Action("Upload", "Image")',
data: formData,
processData: false,
contentType: false,
type: "POST",
success: function () {}
errors: function () {}
});
});
This is one approach; taken from there:
$('#btnUpload').click(function () {
// Checking whether FormData is available in browser
if (window.FormData !== undefined) {
var fileUpload = $("#FileUpload1").get(0);
var files = fileUpload.files;
// Create FormData object
var fileData = new FormData();
// Looping over all files and add it to FormData object
for (var i = 0; i < files.length; i++) {
fileData.append(files[i].name, files[i]);
}
// Adding one more key to FormData object
fileData.append('username', ‘Manas’);
$.ajax({
url: '/Home/UploadFiles',
type: "POST",
contentType: false, // Not to set any content header
processData: false, // Not to process data
data: fileData,
success: function (result) {
alert(result);
},
error: function (err) {
alert(err.statusText);
}
});
} else {
alert("FormData is not supported.");
}
});
Another approach is to use the FileReader class to read the uploaded file, convert it to a base 64 string. Then you can send the base 64 string to the server.
I am new to Knockout JS, I am trying to bind the ajax result data to Knockout JS viewmodel, but I am facing the problem while binding the data to view, I have create model and viewmodel and I am getting the result from ajax. Need help.
Below is my code:
// ajax on page load///
$.ajax({
type: "POST",
dataType: "json",
url: baseUrl + 'api/xxx/xxx',
data: UserProfileModel,
success: function(data) {
result = data;
////view model////
userDetailsViewModel(result);
},
error: function(error) {
jsonValue = jQuery.parseJSON(error.responseText);
//jError('An error has occurred while saving the new part source: ' + jsonValue, { TimeShown: 3000 });
}
});
//// view model///
var userDetailsViewModel = function(result) {
console.log(result);
self = this;
self.user = ko.observable(new userModel(result));
};
$(document).ready(function() {
ko.applyBindings(userDetailsViewModel());
});
/// Model////
function userModel(result) {
this.name = ko.observable();
this.userName = ko.observable();
}
Your userDetailsViewModel is a function that returns undefined. You'll either have to use new or create a return statement if you want it to create an actual viewmodel. E.g.:
var UserDetailsViewModel = function(result) {
var self = this;
self.user = ko.observable(new UserModel(result));
};
var mainUserDetailsViewModel = new UserDetailsViewModel(data);
You'll have to store a reference if you want to update your viewmodel from the scope of the ajax callback. Alternatively, you could make the ajax functionality part of the viewmodel. The easiest example:
var mainUserDetailsViewModel = new UserDetailsViewModel(data);
$.ajax({
success: function(data) {
mainUserDetailsViewModel.user(new UserModel(data));
}
});
Make sure you use this model in your applyBindings call:
ko.applyBindings(mainUserDetailsViewModel);
Note that I've used CapitalizedNames for functions that need to be instantiated, and uncapitalizedNames for instances of those functions. Following default naming conventions might help keeping track of what's what.
I have a website that has the possibility to add comments which are just stored temporarily, when you reload the page the comments are gone. I save the comment data in an ObservAbleArrayList using knockout and javascript. One idea I had was to send this ObservAbleArrayList to my server, store it and then when the page is reloaded the stored arraylist would first update the commentfield. How could I do this with AJAX and PHP?
Here is my javascriptcode for the comments:
function Comment() {
var self = this;
self.nickname = ko.observable();
self.newMsg = ko.observable("");
self.editable = ko.observable(false);
self.addComment = function () {
vm.comments.push(self);
vm.selectedComment(new Comment());
};
self.deleteComment = function () {
vm.comments.remove(self);
};
self.editComment = function () {
self.editable(!self.editable());
};
}
function ViewModel() {
var self = this;
self.comments = ko.observableArray();
self.selectedComment = ko.observable(new Comment());
}
var vm = new ViewModel();
ko.applyBindings(vm);
});
Any help or examples would be very helpful! Thanks in advance.
Send the data as JSON to the server using jQuery as your bridge to handle the server side interaction with it's $.ajax() wrapper.
First, you need to mutate the data into a JSON object to be sent over and easily parsed. In knockout, you can use the .toJSON(model) method on a ko object to get the JSON interpretation of it, such as:
var jsonData = ko.toJSON(ViewModel);
Which will give you your JSON String. This is ready to be passed to the server, so now you can construct your $.ajax() call to your PHP script.
$.ajax({
url: '/path/to/my/script.ext',
type: 'GET', //default anyway, provided for clarity
dataType: 'json', //the returned data from the server will be automatically parsed as json
data: jsonData, //the KO model we converted earlier
success: function(data){
//the server's response is in "data" above, jsonParsed already.
}
});
var values = $('#form_field').serialize();
dataS = "val="+values;
$.ajax({
type: "POST",
url: URL,
data: dataS,
cache: false,
dataType: 'json',
success: function(response)
{ },
});
But the form has an (<input type="file"> field) how do I pass the file into the form using this ajax serialization method. When I print $_FILES doesn't getting any output.
You can't past the $_FILES variable when using ajax. I can assure you that. Thanks.
Ajax does not support file uploads, you should use iframe instead.
you can check further detail here
Posting via Ajax file upload isn't straight forward. First, it isn't directly doable using XHR1.
There are two main ways to do the uploads, using a frame and using the XHR2 spec and the FormData object. This is the method I would recommend.
The easiest way to do this is then to perform two uploads. I'm going to borrow some code here, from GitHub user optikalefx to show you how to do it using jQuery:
// Ajax File upload with jQuery and XHR2
// Sean Clark http://square-bracket.com
// xhr2 file upload
// data is optional
$.fn.upload = function(remote,data,successFn,progressFn) {
// if we dont have post data, move it along
if(typeof data != "object") {
progressFn = successFn;
successFn = data;
}
return this.each(function() {
if($(this)[0].files[0]) {
var formData = new FormData();
formData.append($(this).attr("name"), $(this)[0].files[0]);
// if we have post data too
if(typeof data == "object") {
for(var i in data) {
formData.append(i,data[i]);
}
}
// do the ajax request
$.ajax({
url: remote,
type: 'POST',
xhr: function() {
myXhr = $.ajaxSettings.xhr();
if(myXhr.upload && progressFn){
myXhr.upload.addEventListener('progress',function(prog) {
var value = ~~((prog.loaded / prog.total) * 100);
// if we passed a progress function
if(progressFn && typeof progressFn == "function") {
progressFn(prog,value);
// if we passed a progress element
} else if (progressFn) {
$(progressFn).val(value);
}
}, false);
}
return myXhr;
},
data: formData,
dataType: "json",
cache: false,
contentType: false,
processData: false,
complete : function(res) {
var json;
try {
json = JSON.parse(res.responseText);
} catch(e) {
json = res.responseText;
}
if(successFn) successFn(json);
}
});
}
});
};
I should also note that browser support for this is a little more limited, despite XHR2 being around for 2 years now: https://developer.mozilla.org/en-US/docs/Web/API/FormData contains more information as well as a Browser compatibility chart.
I want to upload file using ajax to an ASP.Net MVC Controller
My JavaScript code looks like this, basically every time the user changed the input value, it will automatically upload the file to the controller
var formdata = false;
if (window.FormData) {
formdata = new FormData();
}
$('input[name=default_export_filename]').change(function() {
var i = 0, len = this.files.length, img, reader, file;
for ( ; i < len; i++ ) {
file = this.files[i];
if (file.type.match(/spreadsheet/) || file.type.match(/ms-excel/)) {
if ( window.FileReader ) {
reader = new FileReader();
reader.onloadend = function (e) {
//showUploadedItem(e.target.result, file.fileName);
};
reader.readAsDataURL(file);
}
if (formdata) {
formdata.append("default_export_filename", file);
}
}
}
if (formdata) {
$.ajax({
url: root + mod + '/uploaddef',
type: "POST",
data: formdata,
processData: false,
contentType: false,
success: function (res) {
console.log(res);
}
});
}
});
In the controller side, I couldn't catch that file using this
[HttpPost]
public ActionResult UploadDEF(HttpPostedFileBase file)
{
var jsonData = new
{
response = 3
};
return Json(jsonData); ;
}
Or this
[HttpPost]
public ActionResult UploadDEF(FormCollectionfile)
{
var jsonData = new
{
response = 3
};
return Json(jsonData); ;
}
The file argument is always null. But in the firebug, I can see that the browser is posting the file to the server. With what object should I catch the file?
I realize the FormData object is not supported in older browser, but my clients are all using the latest browser. So that is not a problem.
The problem is solved.
It turns out I don't have to give any parameters to the controller's method. The files are available through Request object.
Thanks all
You can't use ajax POST to upload a file like this, must be a form submit.
Have a look at component like AjaxUploadify or similar.