I want to achieve the functionality that I am showing in that image. Users will click on the edit buttons and activate the forms for single fields in the page.
Using Ajax I will update a model and will restore the display again after success. The problem here is CSRF_TOKEN. After the user updates the first field, no matter which one it is, the second edit will return an error 500 token mismatch message.
I tried many different approaches, including the last one I saw here:
$.ajaxSetup( {
beforeSend: function( xhr, settings ){
function getCookie( name ){
var cookieValue = null;
if( document.cookie && document.cookie != '' ){
var cookies = document.cookie.split( ';' );
for( var i = 0; i < cookies.length; i++ ){
var cookie = jQuery.trim( cookies[ i ] );
// Does this cookie string begin with the name we want?
if( cookie.substring( 0, name.length + 1 ) == ( name + '=' ) ){
cookieValue = decodeURIComponent( cookie.substring( name.length + 1 ) );
break;
}
}
}
return cookieValue;
}
if( !( /^http:.*/.test( settings.url ) || /^https:.*/.test( settings.url ) ) ){
// Only send the token to relative URLs i.e. locally.
xhr.setRequestHeader( "X-CSRFToken", getCookie( 'csrftoken' ) );
}
}
} );
but still I keep getting the error on the second submit. I know all I need to do is renew the value of the token in my meta <meta name="csrf-token" content="{{ csrf_token() }}">
but I ran out of ideas about how to do it and I really want to keep this functionality without page refresh.
Maybe send the new CSRF from the controller which returns the result and use something like $( 'meta[name="csrf-token"]' ).val( response.csrf );
in my successstage in the ajax request?
My answer consists of modifying at least two files, the template where the ajax is executed and the controller that receives the form request. All the methods that receive the actions from the forms in the page should return the updated csrf token and all the fields (if any) that contain the csrf value should also be updated after ajax execution.
First, the blade template will use a general csrf token for all the forms in the document, for this purpose, I added the meta csrf-token to my header section as usual:
<meta name="csrf-token" content="{{ csrf_token() }}">
Now, the ajax script must insert in the form the token and renew the meta after any of the forms in the template are executed:
$( 'body' ).on( 'submit', '.form', function( event ){
event.preventDefault();
let fd = new FormData( $( this )[ 0 ] );
fd.append( '_token', $( 'meta[name="csrf-token"]' ).attr( 'content' ) )
$.ajax( {
url : '{{ route( 'service.update', [ 'service' => $service ] ) }}',
method : 'post',
data : fd,
contentType: false,
processData: false,
success: function( response ){
$( 'meta[name="csrf-token"]' ).attr( 'content', response.token );
//Do all the other things that need to be done...
},
error: function( xhr, status, error ){
console.log( error );
}
} );
} );
Now, the first thing I do is create a FormData
object with the contents of the form and add the csrf value from the meta to the fieldset (fd.append( '_token', $( 'meta[name="csrf-token"]' ).attr( 'content' ) )
).
After execution is finished, my javascript will receive a response variable with a .token value, which contains the new csrf-token value, so I update the old meta value with the fresh one: $( 'meta[name="csrf-token"]' ).attr( 'content', response.token );
.
Now, for the controller side all I need to do is add to the jSon, the token value, in my case it is an array, so I only need to add it like this:
$response[ 'token' ] = $request->session()->token();
return $response;
Now the forms will work fine, thank you