Sharing reCaptcha token between alpine and livewire - laravel

I am trying to build a reCapture component for a Laravel application using Livewire and Alpine and I can't figure out how to pass the recaptcha token value into the livewire component to complete the validation.
If I understand correctly this is because when I submit the form I am setting a hidden input value (recaptchaToken), however, livewire can not access hidden inputs so I need to use wire:model to bind to the data.
How can I pass the recaptchaToken.value in the submitForm() method, or how do I set the $recaptchaToken value with wire:model
<form x-data="{
execute(){
grecaptcha.ready(() => {
grecaptcha.execute('{{ env('RECAPTCHA_SITE_KEY') }}', { action: 'contact' })
.then((token) => {
{{-- how can i bind to wire:model instead of setting input.value ??? --}}
this.$refs.recaptchaToken.value = token;
})
})
}
}"
x-on:submit.prevent="execute" class="flex-col gg"
{{-- how can I access the recaptchaToken.value to pass into the submitForm() method ??? --}}
wire:submit.prevent="submitForm()">
<input wire:model="recaptchaToken" type="hidden" name="recaptchaToken" x-ref="recaptchaToken">
<button type="submit" class="w-fc btn primary">SUBMIT</button>
</form>

Related

Form is not displaying when using csrf token

When the onclick function in Header.vue is clicked I'm getting this error but when I delete the input tag with csrf_token from the form in Register.vue, then the register form is showing as it is supposed to.
Although after submitting the inputs by POST I'm left with the standard 419 (Sorry, your session has expired. Please refresh and try again.) Laravel screen.
I'm sure the 419 screen is caused by lack of CSRF token, so my final question is how do I implement it in vue.js?
I'm using Vue.js and Laravel to create a SPA, in my Register.vue component which renders onclick on top of the site I've added CSRF token as follows:
<template>
<form id="registerForm" class="register-container" action="registerUser" method="post">
<input type="hidden" name="_token" value="{{ csrf_token() }}">
<div class="register-container__form">
<div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label">
<input class="mdl-textfield__input" name="email" type="text">
<label class="mdl-textfield__label">Email</label>
</div>
.
.
.
</template>
The onclick function which pops up the registration form is in Header.vue:**
<template>
.
.
.
<nav class="mdl-navigation">
<a class="mdl-navigation__link" href="" v-on:click.prevent="registerPopUp()">Sign In</a>
</nav>
<register-form/>
</template>
<script>
import Register from './Register.vue'
export default {
components: {
'register-form': Register
},
methods: {
registerPopUp: () => {
let loginForm = document.getElementById('loginForm');
let registerForm = document.getElementById('registerForm');
loginForm.style.display = "none";
registerForm.style.display = "block";
window.onclick = (e) => {
if(e.target == registerForm)
registerForm.style.display = "none";
}
}
}
}
</script>
Yeah you can't put blade directives in the vue template, this is why you're form isn't rendering and you're getting that error, you haven't actually selected a form and then you're trying to access a property on it.
If you are using axios to make your requests to the server from js, the default resources/js/bootstrap.js file will register the csrf token with axios, just make sure you still have the csrf token placed into a meta field on your layout like this:
<meta name="csrf-token" content="{{ csrf_token() }}">
If you aren't using axios, you can access the csrf token from that meta field within JS like this:
let token = document.head.querySelector('meta[name="csrf-token"]');
If you really need that hidden field there (maybe you're submitting the form with a regular html submit button and not js) you could put this in the "created()" section of the vue component:
this.csrf_token = document.head.querySelector('meta[name="csrf-token"]');
and then in your template:
<input type="hidden" name="_token" :value="csrf_token">

Laravel vue axios is action method and csrf needed for ajax forms

I am posting a ajax from in Laravel using axios and vue, I have a #click="postData" button in the form that toggles a axios post request:
postData() {
axios({
method: 'post',
url: appJS.base_url + '/comment',
responseType: 'json',
data: comData
})
.then(function(response) {
})
But do I still need to add the action, method and csrf to my form?
<form action="{{ url('/comment') }}" method="POST">
{{ csrf_field() }}
</form>
vs
<form></form>
Everything works fine just using <form></form> but I wonder if there are any pros/cons?
I am making a ajax call in the background since I dont want the whole page to reload
You definitely don't need action and method attributes on form tag, because they are already defined on your axios call.
As for the csrf_field(), you probably still need it, because Laravel has a preconfigured middleware called VerifyCsrfToken. But it depends if you use it or not.
you can using event form in vuejs, you need't using ajax, you can try the following code, but if laravel + vuejs, need add Enable CORS for a Single Route in laravel:https://gist.github.com/drewjoh/43ba206c1cde9ace35de154a5c84fc6d
export default{
data(){
return{
title:"Form Register",
}
},
methods:{
register(){
this.axios.post("http://localhost:8888/form-register",this.formdata).then((response) => {
console.log(response);
});
},
}
}
<form action="" method="post" v-on:submit.prevent="register">
<div class="panel-heading">{{title}}</div>
<div class="form-group">
<button type="submit" class="btn btn-danger">Register</button>
<button type="reset" class="btn btn-success">Reset</button>
</div>
</form>

laravel 5.5 | old() empty in view unless $request->flash() used

I've run into an odd issue where the helper function old() always returns null in a blade view unless $request->flash() is used prior to loading the view. I have never had to do this when using laravel in the past. Did something change or is there something that I have forgotten to set/configure. Below is a simple example of the behavior:
web.php
Route::get('/test', function(){
return view('testView');
});
Route::post('/test', function(Illuminate\Http\Request $request){
$request->flash(); // if uncommented old() works, if commented old() does not work
return view('testView');
});
form in testView.blade.php
<form action="/test" method="POST">
{{csrf_field()}}
<input type="hidden" name="test001" value="001"/>
<input type="hidden" name="test002" value="002"/>
<div class="">
{{old('test001')}}
<br/>
{{old('test002')}}
</div>
<button type="submit">GO</button>
</form>
after form submitted without $request->flash()
after form submitted with $request->flash()
EDIT
Thinking this might have something to do with using a single route name for both post and get methods, the form was changed so to submit via get, and the issue persists. For example:
web.php
Route::get('/test', function(function(Illuminate\Http\Request $request){
return view('testView');
});
form in testView.blade.php
<form action="/test" method="GET">
<input type="hidden" name="test001" value="001"/>
<input type="hidden" name="test002" value="002"/>
<div class="">
{{old('test001')}}
<br/>
{{old('test002')}}
</div>
<button type="submit">GO</button>
</form>
Use redirect back() instead of loading view directly in a post method.
return redirect()->back()->withInput();
You need to flash request data to put old input into session, otherwise old() will return empty result. See official doc here.

Laravel normal login with vuejs using axios returns error Request failed with status code 422

Am trying to use laravel and vue js with axios
Previously this was my login form
<form name="login-form" method="POST" action="{{ route('login') }}">
//username and password fields
</form>
Which works perfectly one can login
Now i would like to use vuejs in a similar way without making my app a single page app
so i have resulted to making a login component
<template>
<form name="login-form">
<input type="email" v-model="email" class="form-control" autofocus>
<input id="password" type="password" v-model="password" required>
<button type="submit"
class="btn btn-dark btn-sm"
#click.prevent="login"
:disabled="disableform">
{{submitted?"Logging you in .....":"Login"}}
</button>
</form>
</template>
Now script part
<script>
export default {
data: () => ({
email :'', password:'', remeberme:true, submitted:false
}),
methods: {
login() {
//do other stuff like disabling buttons...etc
axios.post("/login", {email:this.email, password:this.password})
.then(
(res)=>{
///showing sweet alert then
// settimout for 3 sec the
window.location.href="/dashboard";
},
(err)=>{
//show sweet alert of failed login
}
)
},
}
Now whenever i make the login post request an getting an error
Request failed with status code 422
I would like to proceed with the laravel vuejs workflow(with authentication sessions) but not an api jwt token request format.
What could be wrong or is this possible?
It's a validation error. check you laravel controller regarding post/put function.
Request failed with status code 422
When laravel fail the validation then it appears.
You need to use a csrf_field in your form, like this:
<input type="hidden" name="_token" :value="csrfToken">
And define it in your script:
data() {
return {
csrfToken: ''
}
},
created() {
this.csrfToken = document.querySelector('meta[name="csrf-token"]').content;
}

How to refer laravel csrf field inside a vue template

I have a vue template that contains a form:
<form id="logout-form" :action="href" method="POST" style="display: none;">
{{ csrf_field() }}
</form>
In laravel, forms must have a csrf_field() defined. But within a vue component, the statement {{ csrf_field() }} means that I have a method named csrf_field in my vue instance and I am calling it.
How do I add csrf_field under this circumstance?
If you have the token in the meta tag of your header (view)
<meta name="csrf-token" content="{{ csrf_token() }}">
you could access the token using
data() {
return {
csrf: document.querySelector('meta[name="csrf-token"]').getAttribute('content')
}
}
And add a hidden input field within the form and bind the csrf property to the value like this:
<form id="logout-form" :action="href" method="POST" style="display: none;">
<input type="hidden" name="_token" :value="csrf">
</form>
If you're using axios with Vue2 for your ajax requests you can just add the following (usually in your bootstrap.js file):
window.axios.defaults.headers.common = {
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').getAttribute('content'),
'X-Requested-With': 'XMLHttpRequest'
};
You can use this package: npm install vue-laravel-csrf
Usage: <form v-csrf-token>
This is how i use it:
{!! csrf_field() !!}
Put that in your form.
and in your vue script you can simply
methods: {
submitForm: function(e) {
var form = e.target || e.srcElement;
var action = form.action;
get the form and his action then the data value will be:
data: $(form).serialize()
This works perfectly for me and gives no errors at all.

Resources