I am trying to update an "article" which include title(text), body(textarea) and image(file). All goes well until i tried to implement uploading via ajax. Uploading image through regular Laravel means works great, but with ajax, i hit little snag. First, it refuses to validate file. Second if i comment-out validation for file in controller method, it goes "sort-of" well ... But then check $request->hasFile("image")
doesn't recognize file at all ... so instead writes to db an default one("noimage.jpg"). My other ajax functions work great, only update causing problems.
Here is ajax function:
function ajaksUpdate(){
let token = document.querySelector("meta[name='csrf-token']").getAttribute("content");
console.log("hitUpdate");
//console.log();
let updateForm = document.getElementById("updateForm");
let urlId = window.location.href;
let getaArticleId = urlId.lastIndexOf("/");
let articleId = urlId.substring(getaArticleId+1, urlId.length);
let updateFormElements = {};
updateFormElements.title = updateForm.elements[3].value;
updateFormElements.body = CKEDITOR.instances.ckeditor.getData();//Ovo trece po redu je id polja sa ckeditorom.
updateFormElements.image = updateForm.elements[5].files[0];
//console.log();
/*var myformData = new FormData();
myformData.append('title', updateFormElements.title);
myformData.append('body', updateFormElements.body);
myformData.append('image', updateFormElements.image);
let formData = $('#updateForm').serializeArray();
console.log(updateFormElements);*/
console.log("******");
/*for (var [key, value] of myformData.entries()) {
console.log(key, value);
}*/
$.ajax({
url: '/updateAjax/'+articleId,
enctype: 'multipart/form-data',
type: 'POST',
data: {_token: token , message: "bravo", articleId: articleId, title: updateFormElements.title, body: updateFormElements.body,image:updateFormElements.image},
dataType: 'JSON',
/*cache: false,
contentType: false,
processData: false,*/
success: (response) => {
console.log("success");
console.log(response);
},
error: (response) => {
console.log("error");
console.log(response);
}
});
}
Here is controller method:
public function ajaxUpdate(Request $request)
{
if($request->ajax()){
$article = Article::find($request->articleId);
$validator = \Validator::make($request->all(), [
"title" => "required",
"body" => "required",
'image' => 'image|nullable|max:1999'/*If commented, validation passes.*/
]);
if ($validator->passes()){/*If validation passes, it cannot find 'image'*/
$hasImage = false;
//Handle file upload
if($request->hasFile("image")){
$filenameWithExt = $request->file("image")->getClientOriginalName();
$filename = pathinfo($filenameWithExt, PATHINFO_FILENAME);
$extension = $request->file("image")->getClientOriginalExtension();
$fileNameToStore = $filename."_".time().".".$extension;
$path = $request->file("image")->storeAs("public/images", $fileNameToStore);
}
else{
$fileNameToStore = "noimage.jpg";
}
$article->title = $request->input("title");
$article->body = $request->input("body");
//$article->user_id = auth()->user()->id;
$article->image = $fileNameToStore;
$article->save();
$response = array(
'status' => 'success',
'msg' => "Hello!",
"request" => $request->all(),
"passesValidation" => true,
"article" => $article,
"hasImage" => $hasImage,
);
return response()->json($response);
}
else{
$response = array(
'status' => 'success',
'msg' => "Hello!",
"request" => $request->all(),
"passesValidation" => false,
);
return response()->json($response);
}
}
}
Edit1:
If validation(for image) in controller isn't commented out, it shows string like this: "C:\fakepath\855d671944d2c143ba672010acd04437.jpg". Json if isn't commented out:
{
msg: "Hello!"
passesValidation: false
request:
articleId: "3"
body: "posttext"
image: "C:\fakepath\855d671944d2c143ba672010acd04437.jpg"
message: "bravo"
title: "Post1"
_token: "ZVZ9NDNOcMdgoJgvzYhR9LmrPfh7RfMiM1QJVk9v"
__proto__: Object
status: "success"
__proto__: Object
}
If commented out, json look like this:
{
article: {id: 3, title: "Post1", body: "<p><em>Lorem ipsum</em><strong> </strong>dolor sit…ctum. Duis feugiat facilisis lectus a cursus.</p>", created_at: "2019-06-18 00:23:25", updated_at: "2019-06-25 00:18:37", …}
hasImage: false
msg: "Hello!"
passesValidation: true
request:
articleId: "3"
body: "posttext"
image: "C:\fakepath\855d671944d2c143ba672010acd04437.jpg"
message: "bravo"
title: "Post1"
_token: "ZVZ9NDNOcMdgoJgvzYhR9LmrPfh7RfMiM1QJVk9v"
__proto__: Object
status: "success"
__proto__: Object
}
Edit2:
I also tried:
let formData = new FormData();
formData.append("title", updateFormElements.title);
formData.append("body", updateFormElements.body);
formData.append("image", updateFormElements.image);
and inserting and sending it. It throws "Illegal invocation at .... ". And for whatever reason, formData object is empty when i console.log(formData) it.
Edit3: Even if i add processData: false, in ajax call. But then it throws 419 error ...
Edit4:
I don't know if it's going to help, but my form is in modal, there is no submit button and button with ajaksUpdate() function is the one who submits/updates and it's outside of form. I'm updating with put method, also got csrf in modal's form.
Here is a image of how my form/modal looks like:
Edit5:
As per @pal request: My html is formed dynamically, in another function which is called when document is loaded, in layouts blade:
function ajaksShow(){
let token = document.querySelector("meta[name='csrf-token']").getAttribute("content");
console.log("hit");
//var n = str.lastIndexOf("planet");
let urlId = window.location.href;
let getaArticleId = urlId.lastIndexOf("/");
let articleId = urlId.substring(getaArticleId+1, urlId.length);
console.log(articleId);
$.ajax({
url: '/showAjax',
type: 'POST',
data: {_token: token , message: "bravo", articleId: articleId},
dataType: 'JSON',
success: (response) => {
console.log("success");
console.log(response);
let body = "";
let imageStyle = ";height: 435px; background-position: center top; background-attachment: fixed; background-repeat: no-repeat;background-size:cover;";
let img = response.article.image;
let find = " ";
let rep = new RegExp(find, 'g');
img = img.replace(rep, "%20"); // class="alert alert-danger"
let mymodalDelete = "<div class='modal' id='myModalDelete'><div class='modal-dialog'><div class='modal-content'><div class='modal-header'><h4 class='modal-title'>Do you really want to delete this article?</h4><button type='button' class='close' data-dismiss='modal'>×</button></div><div class='modal-body'>deleting ...</div><div class='modal-footer'><button class='btn btn-outline-danger' style='position: absolute;left:0px; margin-left: 1rem;' onclick='ajaksDelete(this)'>Delete</button><button type='button' class='btn btn-danger' data-dismiss='modal'>Close</button></div></div></div></div>";
let updateForm = "<form method='POST' id='updateForm' enctype='multipart/form-data'><input type='hidden' name='_method' value='PUT'><input type='hidden' name='_token' value='"+token+"'><input id='' type='hidden' name='article_id' value='"+response.article.id+"' /><div class='form-group'><label class='label' for='title'>Title</label><input type='text' class='form-control' name='title' placeholder='Title' value='"+response.article.title+"' required></div><div class='form-group'><label for='body'>Body</label><textarea class='form-control' id='ckeditor' name='body' placeholder='Body' required>"+response.article.body+"</textarea></div><div class='form-group'><input type='file' name='image' id='image'></div></form>";
let mymodalUpdate = "<div class='modal' id='myModalUpdate'><div class='modal-dialog'><div class='modal-content'><div class='modal-header'><h4 class='modal-title'>Do you really want to update this article?</h4><button type='button' class='close' data-dismiss='modal'>×</button></div><div class='modal-body'>"+updateForm+"</div><div class='modal-footer'><button class='btn btn-outline-success' style='position: absolute;left:0px; margin-left: 1rem;' onclick='ajaksUpdate()'>Update</button><button type='button' class='btn btn-info' data-dismiss='modal'>Close</button></div></div></div></div>";
let imageUrl = "/storage/images/"+img;
let html = "<a href='/list' class='btn btn-outline-info btn-sm'>Go Back</a><div class='nextPrev'><a href='/list/"+response.prev+"' class='btn btn-outline-success'><i class='fas fa-arrow-left'></i></a><a href='/list/"+response.next+"' class='btn btn-outline-info'><i class='fas fa-arrow-right'></i></a></div><br><br><div id='single-kv' style='background-image: url("+imageUrl+")"+imageStyle+";background-color: red !important;'></div><div id='single-intro'><div id='single-intro-wrap'><h1> "+response.article.title+"</h1>";
if(response.article.body.length > 400){
body = response.article.body.substring(0, 400)+"<a id='readMore' href='/list/"+response.article.id+"'>...Read more</a>";
}
else{
body = response.article.body;
}
html += body;
html += "<div class='comment-time excerpt-details' style='margin-bottom: 20px; font-size: 14px;'><a href='#gotoprofil'> "+response.user.name+" </a> - "+response.article.created_at+"</div><button id='update' class='btn btn-outline-info btn-sm float-left' data-toggle='modal' data-target='#myModalUpdate' onclick='getCkEditor()'>Update</button><button class='btn btn-outline-danger btn-sm float-right' data-toggle='modal' data-target='#myModalDelete'>Delete</button></div></div><br><hr style='color:whitesmoke; width: 50%;'><div id='single-body'><div id='single-content'>"+response.article.body+"</div></div>"+mymodalDelete+mymodalUpdate;
if(document.getElementById("maine")){
document.getElementById("maine").innerHTML = html;
}
},
error: (response) => {
console.log("error");
console.log(response);
}
});
}
Form is updateForm variable and button that activate it, is in mymodalUpdate variable where it invokes function ajaksUpdate(). Also updateForm is chained in mymodalUpdate with two pluses on each side.
Edit6: I also tried:
let formData = $('#updateForm').serializeArray();
console.log(formData);
but it's only shows csrf, hidden method put field,token, title and body field. No file field?.
Edit7:
Here is two pics that'll(i hope) help to illustrate my point:
Edit8:
Somebody suggested it was a csrf mismatch concerning 419 error, so they suggested to temporary disable csrf to see what's what, here are pics:
and logged and controller method:
Updated with change ajax function. If i add to ajax call
cache: false,
contentType: false,
processData: false,
then it show: "POST http://articleapp.test/updateAjax/3 419 (unknown status)" error. If comment this three lines out, then it shows: "Uncaught TypeError: Illegal invocation at add ..." error. I tried everything. and csrf ain't it because i tried disabling, it sends blank request.
Final edit: I don't know what i did, but file uploading works. Here is ajax function:
function ajaksUpdate(){
let token = document.querySelector("meta[name='csrf-token']").getAttribute("content");
console.log("hitUpdate");
//console.log();
let updateForm = document.getElementById("updateForm");
let urlId = window.location.href;
let getaArticleId = urlId.lastIndexOf("/");
let articleId = urlId.substring(getaArticleId+1, urlId.length);
let updateFormElements = {};
updateFormElements.title = updateForm.elements[3].value;
updateFormElements.body = CKEDITOR.instances.ckeditor.getData();//Ovo trece po redu je id polja sa ckeditorom.
updateFormElements.image = updateForm.elements[5].files[0];
let imginp = document.getElementById("imagex").files;
//console.log(imginp);
var myformData = new FormData();
myformData.append('title', updateFormElements.title);
myformData.append('body', updateFormElements.body);
myformData.append('image', updateFormElements.image);
myformData.append('_token', token);
myformData.append('articleId', articleId);
//let formData = $('#updateForm').serializeArray();
console.log("******");
for (var [key, value] of myformData.entries()) {
console.log(key, value);
}
console.log("======");
$.ajax({
url: '/updateAjax/'+articleId,
enctype: 'multipart/form-data',
type: 'POST',
data: myformData,
dataType: 'JSON',
cache: false,
contentType: false,
processData: false,
success: (response) => {
console.log("success");
console.log(response);
},
error: (response) => {
console.log("error");
console.log(response);
}
});
}
Here is controller method:
public function ajaxUpdate(Request $request)
{
if($request->ajax()){
$article = Article::find($request->articleId);
$validator = \Validator::make($request->all(), [
"title" => "required",
"body" => "required",
'image' => 'image|nullable|max:1999'
]);
if ($validator->passes()){
$hasImage = false;
//Handle file upload
if($request->hasFile("image")){
$filenameWithExt = $request->file("image")->getClientOriginalName();
$filename = pathinfo($filenameWithExt, PATHINFO_FILENAME);
$extension = $request->file("image")->getClientOriginalExtension();
$fileNameToStore = $filename."_".time().".".$extension;
$path = $request->file("image")->storeAs("public/images", $fileNameToStore);
}
else{
$fileNameToStore = "noimage.jpg";
}
$article->title = $request->input("title");
$article->body = $request->input("body");
//$article->user_id = auth()->user()->id;
$article->image = $fileNameToStore;
$article->save();
$response = array(
'status' => 'success',
'msg' => "Hello!",
"request" => $request->all(),
"passesValidation" => true,
"article" => $article,
"hasImage" => $hasImage,
);
return response()->json($response);
}
else{
$response = array(
'status' => 'success',
'msg' => "Hello!",
"request" => $request->all(),
"passesValidation" => false,
);
return response()->json($response);
}
}
}
Just want to leave this here for posterity and to ask others: Did you ever encountered problem, which miraculously resolved, but you've been none the wiser about solution whatever it may be?