I don't understand why I can't access my datas in the callback response of the POST method of Axios.
I'm trying here to print a error message on error server response but it says into the catch error function that "this" is not defined
here is my code :
<template>
<div class="row">
<div class="form-group">
<label for="exampleInputEmail1">Login</label>
<input type="text" v-model="loginForm" class="form-control" id="exampleInputEmail1" placeholder="login">
</div>
<div class="form-group">
<label for="exampleInputPassword1">Password</label>
<input type="password" v-model="passwordForm" class="form-control" id="exampleInputPassword1" placeholder="Password">
</div>
<button #click="submitForm();" class="btn btn-default">Submit</button>
<div class="row" v-if="errorBool" style="color:red;"></div>
</div>
</template>
<script>
import store from '../../store/store.js'
import Vuex from 'vuex'
import axios from 'axios'
export default {
store: store,
name: 'Login',
data () {
return {
msg: 'Welcome to Login page',
passwordForm: 'admin',
loginForm: 'admin',
errorBool: false,
errorMessage : ''
}
},
computed: {
...Vuex.mapGetters([
'authentification'
]),
},
methods: {
...Vuex.mapActions([
'loadToken',
'isAuth',
'isNotAuth'
]),
submitForm : function() {
axios.post('http://127.0.0.1:5000/login', {
name: this.loginForm,
password: this.passwordForm
})
.then((response) => {
this.loadToken({token: response.data.token})
this.isAuth()
this.$router.push('/dashboard')
this.errorBool = false
})
.catch(function (error) {
console.log(this) // undefinided
this.errorBool = true
this.errorMessage = error
this.isNotAuth()
})
}
},
}
</script>
Just like you did for the then callback, you should use an arrow function for the catch callback, otherwise you lose the desired this binding.
The Promises/A+ specs, point 2.2.5 specifies concerning the two then callback arguments:
onFulfilled and onRejected must be called as functions (i.e. with no this value).3.2
3.2 That is, in strict mode this will be undefined inside of them; in sloppy mode, it will be the global object.
This equally applies to catch, which is just an alternative way for using the second argument of then.
So write:
.catch( error => {
console.log(this) // <-- problem solved.
this.errorBool = true
this.errorMessage = error
this.isNotAuth()
})
Related
I am using Vue.js 2 and VeeValidate3 to validate my form. This from is also making an axios call to check if the username is already in use. If so, obviously the validation needs to be false.
So far so good. I also see the error message Dieser Name wird bereits verwendet when I type in a username which is already in use.
However, if I see the error message and nevertheless click the submit button, the error message disappears and I can see the message Submit submitCompleteNormalRegistrationForm which gets printed when the form gets submitted.
The question is, why does the form get submitted also there is an validation error with the name? What I am doing wrong?
Also, how can I set the validation for the name to true when the name is not in use?
This is my code so far:
<template>
<div>
<ValidationObserver ref="completeNormalRegistrationForm" v-slot="{ passes }" class="flex-column flex-grow-1 d-flex w-100">
<form #submit.prevent="passes(submitCompleteNormalRegistrationForm)" id="completeNormalRegistrationForm" class="flex-column flex-grow-1 d-flex w-100">
<div class="backButtonWrapper text-left">
<i id="backButtonRegistrationForm" #click="showLoginForm" class="far fa-arrow-alt-circle-left"></i>
</div>
<div class="form-wrapper margin-auto w-100">
<p class="rubik-bold" style="font-size: 1rem;">Registrieren</p>
<ValidationProvider vid="name" name="Nutzername" rules="required|alpha_dash" v-slot="{ errors }">
<input #keyup="completeNormalRegistrationFormUsernameExists" class="form-control search-username" v-model="registerForm.name" type="text" placeholder="Username">
<span v-if="errors[0]" class="username-invalid-span">{{ errors[0] }}</span>
</ValidationProvider>
<ValidationProvider vid="email" name="E-Mail" rules="required|email" v-slot="{ errors }">
<input class="form-control search-email" v-model="registerForm.email" type="email" placeholder="E-Mail">
<span v-if="errors[0]" class="email-invalid-span">{{ errors[0] }}</span>
</ValidationProvider>
<ValidationProvider vid="confirmation" name="Passwort" v-slot="{ errors }">
<input class="form-control" v-model="registerForm.password" type="password" placeholder="Passwort">
<span v-if="errors[0]" class="password-invalid-span">{{ errors[0] }}</span>
</ValidationProvider>
<ValidationProvider rules="confirmed:confirmation" name="Passwort" v-slot="{ errors }">
<input class="form-control" v-model="registerForm.passwordConfirmation" type="password" placeholder="Passwort wiederholen">
<span v-if="errors[0]" class="password-invalid-span">{{ errors[0] }}</span>
</ValidationProvider>
<button type="submit" class="btn btn-primary btn-big big-letter-spacing text-uppercase rubik-bold login">Anmelden</button>
</div>
</form>
</ValidationObserver>
</div>
</template>
<script>
export default {
name: "NavbarAction",
data() {
return {
registerForm: {
name: '',
email: '',
password: '',
passwordConfirmation: '',
termsAndConditions: false,
},
}
},
methods: {
async completeNormalRegistrationFormUsernameExists() {
const nameValid = await this.usernameExists(this.registerForm.name);
if (nameValid) {
this.$refs.completeNormalRegistrationForm.setErrors({name: 'Dieser Name wird bereits verwendet'});
} else {
console.log('Set name is NOT in use!');
}
},
async usernameExists(name){
return await axios.post(window.routes.usernameExists, {value: name})
.then(r => {
return r.data;
});
},
submitCompleteNormalRegistrationForm(){
console.log('Submit submitCompleteNormalRegistrationForm');
console.log(this);
}
}
}
</script>
UPDATE (working with custom rule now):
extend('unique-email', (value) => {
return axios.post(this.routes.emailExists, { value: value })
.then((r) => {
// If email exists, axios response is true
if(r.data){
return {
valid: false,
data: { message: 'E-Mail wird bereits genutzt' }
};
}else{
return {
valid: true,
};
}
}, (err) => {
return {
valid: false,
data: { message: 'E-Mail wird bereits genutzt' }
};
})
},
)
You need to express your email validator as a vee-validate rule instead of trying to do it yourself on keyup. One of the many undocumented things in vee-validate is that if you return a promise as the result of a validation, vee-validate will handle it correctly, waiting to get the result before allowing validation to pass.
Here's an example to get you started:
mounted() {
extend('unique-email', (value) => {
return this.usernameExists(value)
.then((res) => {
return {
valid: true,
};
}, (err) => {
this.$refs.completeNormalRegistrationForm.setErrors({
name: ['Username already registered']
});
})
}, {
immediate: false
})
}
this is front-end validation. The only thing you can do is disable the button when the form is invalid. There is nothing preventing a smart kid trying to submit a form anyway. The true validation should be serverside.
something like:
<button type="submit" class="btn btn-primary btn-big big-letter-spacing text-uppercase rubik-bold login" :disabled="passes(submitCompleteNormalRegistrationForm)">Anmelden</button>
Finally also found a way to set custom error messages without using $refs:
extend('unique-email', (value) => {
return axios.post(window.laravel.emailExists, { value: value })
.then((r) => {
// If email exists, axios response is true
if(r.data){
return "E-Mail wird bereits genutzt";
}else{
return true;
}
}, (err) => {
return "E-Mail wird bereits genutzt";
})
},
);
I'm learning on how to render HTML contents in Vuejs I'm trying to build a small input component which gets generated from render function. It looks something like this:
export default {
name: "nits-input",
methods: {
},
props: {
label: String,
hint: String,
error: String,
placeholder: String
},
render (createElement) {
//Help action text
let helpText = this.hint ? createElement('span', { class: 'm-form__help' }, this.hint) : ''
//Error Text
let errorText = this.error ? createElement('span', { class: 'm--font-danger' }, this.error) : ''
return createElement('div', { class: ''}, [
createElement('label', this.label),
createElement('input', {
class: 'form-control m-input',
attrs: { type: this.type, placeholder: this.placeholder },
domProps: { value: self.value},
on: {
input: function (event) {
this.$emit('input', event.target.value)
}
}
}),
helpText, errorText
])
}
}
While calling this component I'm doing below:
<div class="form-group m-form__group">
<nits-input
label="Email Address"
type="email"
hint="We'll never share your email with anyone else."
placeholder="Enter email"
v-model="email"
>
</nits-input>
</div>
<div class="form-group m-form__group">
<nits-input
label="Password"
type="password"
placeholder="Enter password"
v-model="password"
>
</nits-input>
</div>
I want the value to be stored into v-model, to check the values are being set properly I'm using a watch function
watch: {
email () {
console.log('Email v-model defined as '+this.email)
},
password() {
console.log('Password v-model defined as '+this.password)
}
}
But this always gives me error:
Uncaught TypeError: Cannot read property '$emit' of null
I've taken the references from This VueJS Documentation Link. Help me out in this. Thanks.
you should use arrow function since you're loosing the scope inside that callback :
on: {
input:(event)=> {
this.$emit('input', event.target.value)
}
}
Hope you are doing great!!
I am working on SailsJS web-app and using actions structure for controllers.
This is my user/login action:
module.exports = {
friendlyName: 'Login',
description: 'Login user.',
inputs: {
email: {
description: 'user email.',
type: 'string',
required: true
},
password: {
description: 'user password.',
type: 'string',
required: true
}
},
exits: {
success: {
statusCode: 200,
responseType: 'view',
},
},
fn: async function (inputs, exits) {
// All done.
return 'hello world';
}
};
This is the login form html code:
<div class="row">
<div class="valign-wrapper">
<div class="col s6 z-depth-4 card-panel">
<form class="col s12 login-form" method="POST" action="/login">
<input type="hidden" name="_csrf" value="<%= _csrf %>" />
<div class="card-content">
<h5 class="card-title center-align">Login</h5>
<div class="row margin">
<div class="input-field col s12">
<input id="email" type="email" class="validate">
<label for="email">Email</label>
<div class="invalid-feedback" v-if="formErrors.emailAddress">Please provide a valid email address.</div>
</div>
</div>
<div class="row margin">
<div class="input-field col s12 right">
<input id="password" type="password" class="validate">
<label for="email">Password</label>
</div>
</div>
<div class="row">
<button class="btn waves-effect waves-light right" type="submit" name="action">Login</button>
</div>
</div>
</form>
</div>
</center>
</div>
</div>
Whenever, I am submitting blank form I get this following error:
{
"code": "E_MISSING_OR_INVALID_PARAMS",
"problems": [
"\"email\" is required, but it was not defined.",
"\"password\" is required, but it was not defined."
],
"message": "The server could not fulfill this request (`POST /login`) due to 2 missing or invalid parameters. **The following additional tip will not be shown in production**: Tip: Check your client-side code to make sure that the request data it sends matches the expectations of the corresponding parameters in your server-side route/action. Also check that your client-side code sends data for every required parameter. Finally, for programmatically-parseable details about each validation error, `.problems`. "
}
Now, the issue is that I am unable to find a way to handle this error in prettier way.
I hope one of you can guide me on the right path.
Thank You,
U can transform error in api/responses/badRequest
module.exports = async function badRequest(data) {
const status = 400;
if (data.code && data.code === 'E_MISSING_OR_INVALID_PARAMS'){
return this.res.status(status).json({
code: 'My code',
message: 'My message',
whateva: 'Bla bla'
});
}
return this.res.status(status).json(data);
};
I have a custom project and I need the same thing u requested. I made npm package who take an error object and make it prettier for frontend. Something like this:
module.exports = async function badRequest(data) {
const formatError = require('myFormatError');
const {status, response}= formatError(data);
return this.res.status(status).json(response);
};
It doesn't need to be npm, u can make some function. I hope u were looking for this.
Using the same error object to display will be a mistake because in production technical errors will be stripped off by the toJson method.
The best way to handle the error is to write a custom one. such as
module.exports = {
friendlyName: 'Login',
description: 'Login user.',
inputs: {
email: {
description: 'user email.',
type: 'string'
},
password: {
description: 'user password.',
type: 'string'
}
},
exits: {
success: {
statusCode: 200,
responseType: 'view',
},
badrequest:{
statusCode: 400,
responseType: 'view',
}
},
fn: async function (inputs, exits) {
try{
if(!inputs.email || !inputs.password){
return exits.badrequest({code: 'PARAM_MISSING', message: 'Email/Password is missing'});
}
// all good
return exits.success({/* object containing information realted to success*/});
}
catch(e){
return exists.error(e);
}
}
}
};
I have been following the Stripe integration tutorial by Laracasts and it's become apparent to me that a lot has changed since Laravel 5.4 was released. I have been able to still find my way along but I have hit a bump trying to submit a payment form using Vue and Axios.
The product is being retrieved from a database and displayed in a select dropdown - this works. My issue is the data is not being properly sent to the store function in the PurchasesController. When I try to make a purchase the form modal appears fine, I fill it out with the appropriate test data and submit it, but in Chrome inspector I can see that /purchases returns a 404 error and when I check the network tab the error is: No query results for model [App\Product]
Here is the original Vue code:
<template>
<form action="/purchases" method="POST">
<input type="hidden" name="stripeToken" v-model="stripeToken">
<input type="hidden" name="stripeEmail" v-model="stripeEmail">
<select name="product" v-model="product">
<option v-for="product in products" :value="product.id">
{{ product.name }} — ${{ product.price /100 }}
</option>
</select>
<button type="submit" #click.prevent="buy">Buy Book</button>
</form>
</template>
<script>
export default {
props: ['products'],
data() {
return {
stripeEmail: '',
stripeToken: '',
product: 1
};
},
created(){
this.stripe = StripeCheckout.configure({
key: Laravel.stripeKey,
image: "https://stripe.com/img/documentation/checkout/marketplace.png",
locale: "auto",
token: function(token){
axios.post('/purchases', {
stripeToken: token.id,
stripeEmail: token.email
})
.then(function (response) {
alert('Complete! Thanks for your payment!');
})
.catch(function (error) {
console.log(error);
});
}
});
},
methods: {
buy(){
let product = this.findProductById(this.product);
this.stripe.open({
name: product.name,
description: product.description,
zipCode: true,
amount: product.price
});
},
findProductById(id){
return this.products.find(product => product.id == id);
}
}
}
</script>
And my PurchasesController.
<?php
namespace App\Http\Controllers;
use Log;
use App\Product;
use Illuminate\Http\Request;
use Stripe\{Charge, Customer};
class PurchasesController extends Controller
{
public function store()
{
Log::info("Product Info: " . request('product'));
Log::info("Stripe Email: " . request('stripeEmail'));
Log::info("Stripe Token: " . request('stripeToken'));
$product = Product::findOrFail(request('product'));
$customer = Customer::create([
'email' => request('stripeEmail'),
'source' => request('stripeToken')
]);
Charge::create([
'customer' => $customer->id,
'amount' => $product->price,
'currency' => 'aud'
]);
return 'All done';
}
}
I realise that product isn't being passed through to /purchases above so I have tried this:
axios.post('/purchases', {
stripeToken: token.id,
stripeEmail: token.email,
product: this.product
})
Unfortunately I still get the same No query results for model [App\Product] error even with that. Is there another/better way of passing data from Vue/Axios that I could use instead? If anyone is able to assist it would be much appreciated.
Thank you in advance.
Edit
The solution was to recast this to be a new variable and it started functioning again. Here is the relevant portion of the Vue Code that worked for me:
created(){
let module = this; // cast to separate variable
this.stripe = StripeCheckout.configure({
key: Laravel.stripeKey,
image: "https://stripe.com/img/documentation/checkout/marketplace.png",
locale: "auto",
token: function(token){
axios.post('/purchases', {
stripeToken: token.id,
stripeEmail: token.email,
product: module.product
})
.then(function (response) {
alert('Complete! Thanks for your payment!');
})
.catch(function (error) {
console.log(error);
});
}
});
},
Are you sure when assigning the product ID to data (using product: this.product) that this keyword is really your Vue instance? You might need to bind it manually calling .bind(this) on the .post(...) call.
See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind
I am using laravel 5.4 and Vue.js 2 to save some information along with file(video) in DB.But i got error like this
error: http://imgur.com/a/7XGERPUT
http://localhost:8000/videos/null 404 (Not Found)
Uncaught (in promise) TypeError: Cannot read property 'uid' of undefined
NotFoundHttpException in Handler.php line 131:
No query results for model [App\Models\Video].
Route:
Route::group(['middleware'=>['auth']],function(){
Route::get('/upload','VideoUploadController#index');
Route::post('/videos','VideoController#store');
Route::put('/videos/{video}','VideoController#update');
Route::get('/channel/{channel}/edit','ChannelSettingsController#edit');
Route::put('/channel/{channel}/edit','ChannelSettingsController#update');
});
Controler:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Http\Requests\VideoUpdateRequest;
use App\Models\Video;
class VideoController extends Controller
{
public function store(Request $request)
{
$uid = uniqid(true);
$channel = $request->user()->channel()->first();
$video = $channel->video()->create([
'uid'=>$uid,
'title'=>$request->title,
'description'=>$request->description,
'visibility'=>$request->visibility,
'video_filename'=>"{$uid}.{$request->extension}",
]);
return response()->json([
'data' => [
'uid' => $uid
]
]);
}
public function update(VideoUpdateRequest $request, Video $video)
{
//authentication checked here .......
$video->update([
'title' => $request->title,
'description' => $request->description,
'visibility' => $request->visibility,
'allow_votes' => $request->has('allow_votes'),
'allow_comments' => $request->has('allow_comments')
]);
if ($request->ajax()) {
return response()->json(null, 200);
}
return redirect()->back();
}
}
Uplod.vue
<template>
<div class="container">
<div class="row">
<div class="col-md-8 col-md-offset-2">
<div class="panel panel-default">
<div class="panel-heading">Example Component</div>
<div class="panel-body">
<input type="file" name="video" id="video" #change="changfileInputChange" v-if="!uploading">
<div id="video-form" v-if="uploading&&!failed">
<div class="form-group">
<label for="title">Title</label>
<input type="" name="" v-model="title" class="form-control">
</div>
<div class="form-group">
<label for="description">Description</label>
<textarea class="form-control"v-model="description"></textarea>
</div>
<div class="form-group">
<label for="visibility">Visibility</label>
<select class="form-control" v-model="visibility">
<option value="private">Private</option>
<option value="public">Public</option>
<option value="unlisted">Unlisted</option>
</select>
</div>
<span class="help-block pull-right">{{saveStatus}}</span>
<button class="btn btn-default" type="submit"#click.prevent="update">Save changes</button>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
mounted() {
console.log('Component mounted.')
},
data()
{
return{
uid:null,
uploading:false,
uplodComplete:false,
failed:false,
title:'unlisted',
description:null,
visibility:'private',
saveStatus:null
}
},
methods:{
changfileInputChange()
{
this.uploading=true;
this.failed=false;
this.file=document.getElementById('video').files[0];
this.storeVideo().then(()=>{
})
},
storeVideo(){
return this.$http.post('/videos',{
title:this.title,
description:this.description,
visibility:this.visibility,
extension:this.file.name.split('.').pop()
}).then((response)=>{
this.uid = response.json().data.uid;
});
},
update() {
this.saveStatus = 'Saving changes.';
return this.$http.put('/videos/' + this.uid, {
title: this.title,
description: this.description,
visibility: this.visibility
}).then((response) => {
this.saveStatus = 'Changes saved.';
setTimeout(() => {
this.saveStatus = null
}, 3000)
}, () => {
this.saveStatus = 'Failed to save changes.';
});
}
}
}
</script>
In first i save video to db along with some default value for title,description etc with ajax request,Please see Video.vue code for this
I am getting uid(unique id for video)in console from store method but when i update by clicking the save chages button i got error like this:
when i used i default uid in controller in
public function store(Request $request)
{
$uid = '158cfff622e1b7';
//....some code here
//finally return $uid
return response()->json([
'data' => [
'uid' => $uid
]
]);
}
and in Upload.vuein this line if i change uid to default 158cfff622e1b7,it works fine ie:result updated
return this.$http.put('/videos/' +'158cfff622e1b7', {
//code
});
it means i am not getting `uid` ,or something is wrong,please help me
Here is screenshot of error: http://imgur.com/a/7XGER
Error: PUT http://localhost:8000/videos/null 404 (Not Found)
I can see you are using route model binding.
Route::put('/videos/{video}','VideoController#update');
Route model binding works out of the box with ID field. If you use a different key, you need to tell your model to use that key for route model binding.
In Video model add
public function getRouteKeyName() {
return 'uid';
}
Update
In providers/RouteServiceProvider, add this inside boot()
Route::bind('video', function ($value) {
return App\Models\Video::where('uid', $value)->first();
});
If it still does not work, simply get the video and update it, the good old way
public function update(VideoUpdateRequest $request, $uid)
{
$video = Video::where('uid', $uid)->first();
$video->title = $request->title;
$video->description = $request->description;
...
$video->update();
if ($request->ajax()) {
return response()->json(null, 200);
}
return redirect()->back();
}
Replace this this.uid = response.json().data.uid; by this.uid = response.body.data.uid;
it must work,if not let me hear
Read Docs for more