I can't send file through an ajax PUT request, using Laravel - ajax

I have a form with an input of type file hidden. An image tag works as the clickable trigger to select the file itself, with js to trigger it (that works), but I want to automatically make a PUT request as one chooses an image instead of having to click a submit button on the form, every time the image is changed on the input field. I'm using ajax for that, but at the controller endpoint that processes the request, I don't seem to have any file. If I put other fields such as textual they seem to pass into the controller through the request just fine, though.
My route:
Route::put('/coins/image/{key}', [CoinController::class, 'image'])->name("coins.image");
My controller (no actual image-updating code yet; just what I'm doing to check for the file):
public function image(int $key)
{
dump(request()->file('file'));
dump(request()->file);
dump(request());
}
My HTML and JS in the following snippet:
function promptImageForUpload(elemId)
{
$('#' + elemId).click();
}
function uploadImage(event, imgId, key)
{
event.preventDefault();
var inputElem = event.target;
var imageFile = inputElem.files[0];
var imgElem = $('#' + imgId);
var form = $(inputElem).parent();
var formData = new FormData(form[0]);
formData.append('file', imageFile);
$.ajax({
url: "/coins/image/" + key,
type: "PUT",
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
},
data: formData,
enctype: 'multipart/form-data',
processData: false,
contentType: false,
success: function(response) {
var reader = new FileReader();
reader.onload = function(e) {
imgElem.attr("src", e.target.result);
};
reader.readAsDataURL(imageFile); // convert to base64 string
}
});
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="card-body">
<form data-key="0" id="coin-header-0_form" method="POST" action="#" enctype="multipart/form-data">
<input type="file" name="file" id="coin-header-0_file" style="z-index:-1; width: 100%; height: 100%; display: none;" onchange="uploadImage(event, 'coin-header-0_preview', 0)">
<input type="hidden" name="_method" value="PUT">
<meta name="csrf-token" content="7c3s2NmosdK9qzrS10xGAB0rYXw5g41azRjcmPQC">
<img src="http://snapbuilder.com/code_snippet_generator/image_placeholder_generator/60x40/007730/DDDDDD/this%20pict" width="50px" height="50px" alt="icon" id="coin-header-0_preview" onclick="promptImageForUpload('coin-header-0_file');">
</form>
</div>
For now, I am force-updating the preview of the image on the ajax success instead of retrieving it from the updated entity upon successful update, to avoid a second query.
I have the method set to PUT for spoofing in the ajax request and the xsrf token set the headers as well.
Enctype is set to multipart/form-data for files too. I have no clue as to why I don't see the uploaded file anywhere in the request.
Using Laravel Framework 8.25.0, jquery 3.5.1.
Please let me know if any more info is needed, anyone.
I couldn't find any solution on any stackoverflow entry or elsewhere that regards this subject.
Any help is greatly appreciated since I don't really how else to look at this in a debugging approach.

HTML forms do not support PUT, PATCH or DELETE actions. So, when defining PUT, PATCH or DELETE routes that are called from an HTML form, you will need to add a hidden _method field to the form. The value sent with the _method field will be used as the HTTP request method:
<form action="/foo/bar" method="POST">
<input type="hidden" name="_method" value="PUT">
<input type="hidden" name="_token" value="{{ csrf_token() }}">
</form>
You may use the method_field helper to generate the _method input:
{{ method_field('PUT') }}
In the same way, this check may also affect ajax forms. Then in your form switch to the POST method and set the PUT parameter for it to work.

Related

Play framework write Action with Ok(...) that doesn't load new page

Play framework 2.4.x. A button is pressed on my home page that executes some code via Ajax, and returns its results beneath the button without loading a new page. The results wait for a user to input some text in a field and press "submit". Those results Look like this:
<li class="item">
<div>
<h3>Email: </h3>
<a>#email.tail.init</a>
<h3>Name: </h3>
<a>#name</a>
</div>
<div>
<h3>Linkedin: </h3>
<form class="linkedinForm" action="#routes.Application.createLinkedin" method="POST">
<input type="number" class="id" name="id" value="#id" readonly>
<input type="text" class="email" name="email" value="#email" />
<input type="text" class="emailsecondary" name="emailsecondary" value="" />
<input type="text" class="name" name="email" value="#name" />
<input type="text" class="linkedin" name="linkedin" value="" />
<input type="submit" value="submit" class="hideme"/>
</form>
</div>
<div>
<form action="#routes.Application.delete(id)" method="POST">
<input type="submit" value="delete" />
</form>
</div>
</li>
Along with some jquery that slides up a li after submission:
$(document).ready(function(){
$(".hideme").click(function(){
$(this).closest('li.item').slideUp();
});
});
However, since a form POST goes inside an Action that must a return an Ok(...) or Redirect(...) I can't get the page to not reload or redirect. Right now my Action looks like this (which doesn't compile):
newLinkedinForm.bindFromRequest.fold(
errors => {
Ok("didnt work" +errors)
},
linkedin => {
addLinkedin(linkedin.id, linkedin.url, linkedin.email, linkedin.emailsecondary, linkedin.name)
if (checkURL(linkedin.url)) {
linkedinParse ! Linkedin(linkedin.id, linkedin.url, linkedin.email, linkedin.emailsecondary, linkedin.name)
Ok(views.html.index)
}else{
Ok(views.html.index)
}
}
)
Is it possible to return Ok(...) without redirecting or reloading? If not how would you do a form POST while staying on the same page?
EDIT: Here is my attempt at handling form submission with jquery so far:
$(document).ready(function(){
$(".linkedinForm").submit(function( event ) {
var formData = {
'id' : $('input[name=id]').val(),
'name' : $('input[name=name]').val(),
'email' : $('input[name=email']).val(),
'emailsecondary' : $('input[name=emailsecondary]').val(),
'url' : $('input[name=url]').val()
};
jsRoutes.controllers.Application.createLinkedin.ajax({
type :'POST',
data : formData
})
.done(function(data) {
console.log(data);
});
.fail(function(data) {
console.log(data);
});
event.preventDefault();
};
});
This is an issue with the browser's behavior on form submission, not any of Play's doing. You can get around it by changing the behavior of the form when the user clicks submit.
You will first want to attach a listener to the form's submission. You can use jQuery for this. Then, in that handler, post the data yourself and call .preventDefault() on the event. Since your javascript is now in charge of the POST, you can process the data yourself and update your page's HTML rather than reloading the page.
What you need is use ajax to submit a form, check this: Submitting HTML form using Jquery AJAX
In your case, you can get the form object via var form = $(this), and then start a ajax with data from the form by form.serialize()
$.ajax({
type: form.attr('method'),
url: form.attr('action'),
data: form.serialize(),
success: function (data) {
alert('ok');
}
});
In order to accomplish this task, i had to use play's javascriptRouting
This question's answer helped a lot.
I'm not experienced with jquery so writing that correctly was difficult. For those that find this, here is my final jquery that worked:
$(document).ready(function(){
$("div#results").on("click", ".hideme", function(event) {
var $form = $(this).closest("form");
var id = $form.find("input[name='id']").val();
var name = $form.find("input[name='name']").val();
var email = $form.find("input[name='email']").val();
var emailsecondary = $form.find("input[name='emailsecondary']").val();
var url = $form.find("input[name='url']").val();
$.ajax(jsRoutes.controllers.Application.createLinkedin(id, name, email, emailsecondary, url))
.done(function(data) {
console.log(data);
$form.closest('li.item').slideUp()
})
.fail(function(data) {
console.log(data);
});
});
});
Note that my submit button was class="hideme", the div that gets filled with results from the DB was div#results and the forms were contained within li's that were class="item". So what this jquery is doing is attaching a listener to the static div that is always there:
<div id="results">
It waits for an element with class="hideme" to get clicked. When it gets clicked it grabs the data from the closest form element then sends that data to my controller via ajax. If the send is successful, it takes that form, looks for the closest li and does a .slideUp()
Hope this helps

Easiest way of converting a regular from to perform submissions via ajax?

I'm trying to convert simple forms such as:
<form action="/api.php", method="get">
... radios, checkboxes etc
<input type="submit" value="Submit">
</form>
To perform ajax submission instead of reloading the page when hitting submit using angular.
I plan on removing the submit button and replacing it with a regular button with ng-click="submit()". My submit function would look something like this:
$scope.submit = function() {
$http.get('/api', { params: ??? })
.success(...));
}
However the difficulty I have here is attaching the get params from my form inputs. I'm not sure how to reference them. Would I have to add ng-model to every single input element?
I have a lot of forms that require "converting" and I was wondering what would be the least intrusive way (least changes to markup) to do this? The reason is because a previous developer has left me with a soup of ugly html changing things will be costly.
Yes, its EASY.
html
<form action="/api.php" ng-submit="submit()">
<input type="text" name="name" ng-model="user.name">
<input type="submit" value="Submit">
</form>
js
$scope.submit = function() {
$scope.user = {};
$http({
method: 'GET',
url: '/api.php?name=' + $scope.user.name
}).
success(function(data, status, headers, config) {
console.log(JSON.stringify(data));
});
};

Using ajax to pass text to a db

I am trying to take simple text from a form, pass it to my controller via ajax, and have that send to the data base.
View
<form method="POST">
Email: <input type="text" name="email" id="email">
Question: <input type="text" name="qText" id="qText">
<input id="rate" type="submit">
</form>
<script type = "text/javascript">
$(function(){
$("#rate").click(function(){
dataString = $("#email").serialize();
$.ajax({
type: "POST",
url: "<?php echo base_url();?>index.php/trial/insert_into_db",
data: dataString,
});
});
});
</script>
The controller code and the model code work fine. I am almost sure that it is the ajax code that is not working.
Any information would be greatly appreciated!
Thank you.
One thing missing from your posted code is disabling the default form submission. There still could be other issues.
You don't specify an action so by default the action is the same url as the page.
<form method="POST">
You are doing AJAX but you have not disabled the default behavior with return false or event.preventDefault
$("form").submit(function(event) {
event.preventDefault();
// or
return false;
});
I prefer preventDefault() but the point is you need to prevent the default browser behavior.
Edit: This is how I would submit the form with AJAX.
If you had more than one form button to consider then
$("form").submit(function(e) {
e.preventDefault();
});
$("#rate").click(function(e) {
$.ajax({ ... });
});
But it's (marginally) easier to do it with one handler. I'd also stick the action on the form so the form still submits to the correct url if the javascript failed.
<form id="myform" action="<?php echo base_url();?>index.php/trial/insert_into_db" method="post">
Instead of handling the button click handle the form submission
$("#myform").on("submit", function(e) {
e.preventDefault();
var formData = $(this).serialize();
$.ajax({
type = "post",
url = $(this).attr("action"),
data = formData
})
.done(function(result) {
// do something with the response
});
});

Cross Domain Ajax Issue

I have two files
1) index.php(picks data from the code editor and submits for processing via Jquery Ajax to exec.php)
2) exec.php (currently just transfer the data it recieved via index.php using jsonp)
Code of index.php
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script>
function test() {
var code = document.getElementById('code').value;
var code_data = "code=" + code;
alert(code_data);
$.ajax({
type: "POST",
crossDomain: true,
url: "http://code1.guru99.com/exec.php",
data: code_data,
dataType: "jsonp",
success: function (data) {
alert(data);
}
});
alert("End of Test");
}
</script>
<form name="myform" id="myform" method="POST" class="code-box">
<textarea name="code" id="code"><?
$code='<?php
"Hello";
?>';
echo $code;
?>
</textarea> <!-- for add html tag in text area nad print the code-->
<div class="hint">This code is editable. Click Run to execute.</div>
<input type="submit" value="Run" id="submit" onClick="test();"><!--<img id="ajax-loader" name="ajax-loader" src="/img/ajax-loader.gif" class="hidden" style="vertical-align:middle" />-->
</form>
<div name="label" id="label"> </div>
<div name="out" id="out"> </div>
Code of exec.php
<?php
$code=$_POST['code'];
$fp=fopen("file.txt","w"); // Storing the data into a file just to know that data is passed
fwrite($fp,$code);
fclose($fp);
header('Content-Type: application/jsonp');
echo $_GET['callback']."(".json_encode($code).");"
?>
The problem is data just does not pass into exec.php. I am not sure why...
The code is live at http://code.guru99.com/php/
Please help...
You cannot use AJAX to do this. Instead consider posting from a hidden Iframe using a regular FORM and setting the action to the URL you desire. You can still submit the form using JavaScript.
You can also listen to the onload event on the iframe to detect when your post has completed.
Alternately, you can use a server-side proxy.
The code syntax is correct.
May the problem could be with your server

jQuery Mobile avoid changePage() in ajax form submit

I have a post form, I want the submit to be done in an AJAX way but I don't want the page URL to change.
For example:
<form action="/countries/change_country" data-ajax="true" method="post">
<select name="country_code">
<option value="FR" selected="selected">France</option>
<option value="DE">Germany</option>
</select>
<input type="submit" value="change country" />
</form>
When I click in the change country button the form is sent through AJAX what is nice but the page URL is changed to /countries/change_country what is not nice because this URL doesn't exist in my server which is very picky with HTTP verbs.
I know it is possible to change this default behavior for the whole application but I would like to deactivate the changePage() only for this form.
Submit the form through Javascript/jQuery instead.
First: Disable the default submit-behaviour
$('#form').on('submit', function (e) {
if (e.preventDefault) e.preventDefault();
return false;
});
Second: Serialize the form data with jQuery
var serializedFormData = $('#form').serialize();
Third: Post your form with $.ajax();
$.ajax({
url: '/countries/change_country',
type: 'POST',
data: serializedFormData
});

Resources