I'm creating login validation in Vue.js but the error message is not displaying and it gives me the error:
Property or method "error" is not defined on the instance but referenced during render. Make sure that this property is reactive, either in the data option, or for class-based components, by initializing the property.
Any help?
Template:
<template>
<div class="container" width="900">
<div class="row justify-content-center" style="margin-top: 10px;">
<div class="col-5">
<div v-if="error" class="alert alert-danger" role="alert">
{{error}}
</div>
<div class="card" >
<div class="card-text">
<div class="form-group" #keyup.enter="logItIn">
<input class="form-control"
v-model="login.email"
label="Email"
placeholder="Email Address"
required
> <br>
<input class="form-control"
v-model="login.password"
label="Password"
type="password"
placeholder="Password"
required>
</div>
</div>
<button type="button" class="btn btn-secondary" #click='logItIn'>Login</button>
</div>
</div>
</div>
</div>
</template>
Script:
import axios from 'axios'
export default {
data() {
return {
login: {
email:"",
password:"",
error: ""
}
}
},
methods: {
async logItIn() {
try {
axios.post('https://odevin-api.herokuapp.com/login',this.login)
.then(response => {
console.log(response)
let newToken=response.data.response.token;
window.token=newToken;
let user=response.data.response.user; //response
localStorage.setItem('token',newToken);
localStorage.setItem('user',JSON.stringify(user));
window.axios.defaults.params={api_token:newToken}
// Event.$emit('login',user);
this.$router.push('/');
});
} catch(e) {
this.error = 'invalid user name or password';
}
}
}
}
you referenced {{ error }} in your template but in your data object, error is a property of login object. so vue can't find it properly.
either change the usage in your template to {{ login.error }} or define it in your data object like this:
data() {
return {
error: '',
login: {
email: '',
password: '',
},
}
}
Related
So for form actions, I made a class to make things cleaner, but there's a problem. Whenever any errors/success messages are set in the class, Svelte does not automatically rerender the changes.
Frontend:
<script>
import Form from '#/periphs/form'
let changePasswordForm = new Form()
changePasswordForm.form.values = {
newPassword: null,
currentPassword: null
}
changePasswordForm.params.endpoint = route('api.user.settings.security.change-password')
const changePassword = () => {
changePasswordForm.execute()
}
</script>
<div class="block">
<div class="is-size-6 has-text-weight-light">Change Password</div>
<div class="field help has-text-grey-light">
1. Use special characters. (!##$%^&*)
<br>2. Do not reuse passwords.
<br>3. Do not use passwords that can be guessed. (Birthdays, names, numbers, etc).
</div>
<fieldset disabled={ changePasswordForm.form.processing }>
<div class="field">
<div class="control">
<label for="" class="label is-small">New Password</label>
<input bind:value={ changePasswordForm.form.values.newPassword } class:is-danger={ changePasswordForm.form.errors.newPassword } type="password" class="input is-small" placeholder="New Password">
</div>
{#if changePasswordForm.form.errors.newPassword}
<div class="help is-danger">{ changePasswordForm.form.errors.newPassword }</div>
{/if}
</div>
<div class="field">
<div class="control">
<label for="" class="label is-small">Current Password</label>
<input bind:value={ changePasswordForm.form.values.currentPassword } class:is-danger={ changePasswordForm.form.errors.currentPassword } type="password" class="input is-small" placeholder="Current Password Password">
</div>
{#if changePasswordForm.form.errors.currentPassword}
<div class="help is-danger">{ changePasswordForm.form.errors.currentPassword }</div>
{/if}
</div>
<div class="field">
<button on:click={ () => changePassword() } class="button is-primary is-small">Change Password</button>
{#if changePasswordForm.form.success.message}
<div class="help is-success">{ changePasswordForm.success.message }</div>
{/if}
{#if changePasswordForm.form.errors._flood}
<div class="help is-danger">{ changePasswordForm.errors._flood }</div>
{/if}
</div>
</fieldset>
</div>
Form.js:
'use strict'
import axios from "axios"
export default class Form {
constructor() {
this.form = {
processing: false,
success: {},
errors: {},
values: {}
}
this.params = {
method: 'POST',
endpoint: null,
}
}
getErrors() {
return this.form.errors
}
execute() {
/*
Before form submission, reset all errors/successes
*/
this.form.processing = true
this.form.success = {}
this.form.errors = {}
if (this.params.method == 'POST') {
axios.post(this.params.endpoint).then(r => {
/*
Handle success
*/
this.form.errors = {}
this.form.success = r.data
}).catch(e => {
/*
422
Form errors (Validation)
*/
if (e.response.status == 422) {
this.form.errors = e.response.data.errors
}
/*
429
Request timeout (Flood)
*/
if (e.response.status == 429) {
this.form.errors = { ...this.form.errors, ...{ '_flood': e.response.data.message } }
}
console.log(this.form.errors) // SHOWS ERRORS AS ITS SUPPOSED TO, BUT THIS IS NOT REPRODUCED ON THE FRONTEND
}).finally(() => {
this.form.processing = false
})
}
}
}
Whenever there is an error/success, it should show those changes on my front end, but that is not the case. Any one got any idea on how to fix this?
Reactivity only works for code within components, outside you need to use stores or events to signal changes.
I would like to show the backend error messages on my Vue component, so for doing that I have created this component:
<template>
<section class="p-0 d-flex align-items-center">
<div class="container-fluid">
<div class="row">
<!-- Right START -->
<div class="col-md-12 col-lg-8 col-xl-9 mx-auto my-5 position-relative">
<!-- Shape Decoration END -->
<div class="row h-100">
<div
class="
col-md-12 col-lg-10 col-xl-5
text-start
mx-auto
d-flex
align-items-center
"
>
<div class="w-100">
<h3>Sign up for your account!</h3>
<p>
Join us today! Create your account easily with less
information.
</p>
<!-- Form START -->
<form class="mt-4" #submit.prevent="submit">
<!-- Email -->
<div class="mb-3">
<label class="form-label" for="email">Email address</label>
<input
v-model="data.name"
v-bind:class="{ 'is-invalid': validate.email }"
required
type="email"
class="form-control"
id="email"
aria-describedby="emailHelp"
placeholder="E-mail"
/>
<div class="invalid-feedback">
{{ validate.email }}
</div>
<small id="emailHelp" class="form-text text-muted"
>We'll never share your email with anyone else.</small
>
</div>
<!-- Username -->
<div class="mb-3">
<label class="form-label" for="email">Username</label>
<input
v-model="data.username"
v-bind:class="{ 'is-invalid': validate.username }"
required
type="text"
class="form-control"
id="username"
placeholder="Username"
/>
<div class="invalid-feedback">
{{ validate.username }}
</div>
</div>
<!-- Password -->
<div class="mb-3">
<label class="form-label" for="password">Password</label>
<input
v-model="data.password"
v-bind:class="{ 'is-invalid': validate.password }"
required
type="password"
class="form-control"
id="password"
placeholder="*********"
/>
<div class="invalid-feedback">
{{ validate.password }}
</div>
</div>
<!-- Password -->
<div class="mb-3">
<label class="form-label" for="password2"
>Confirm Password</label
>
<input
v-model="data.password2"
v-bind:class="{ 'is-invalid': validate.password2 }"
type="password"
class="form-control"
id="password2"
placeholder="*********"
/>
<div class="invalid-feedback">
{{ validate.password2 }}
</div>
</div>
<!-- Checkbox -->
<div class="mb-3 form-check">
<input
type="checkbox"
class="form-check-input"
id="remember"
/>
<label class="form-check-label" for="remember"
>keep me signed in</label
>
</div>
<!-- Button -->
<div class="row align-items-center">
<div class="col-sm-4">
<button type="submit" class="btn btn-dark btn-line">
Sign me up
</button>
</div>
<div class="col-sm-8 text-sm-end">
<span class="text-muted"
>Already have an account?
Signin here</span
>
</div>
</div>
</form>
<!-- Form END -->
<div class="bg-dark-overlay-dotted py-2 my-4"></div>
</div>
</div>
</div>
</div>
<!-- Right END -->
</div>
</div>
</section>
</template>
<script lang="ts">
import { defineComponent, reactive, ref } from "vue";
import { useRouter } from "vue-router";
export default defineComponent({
name: "Register",
setup(props) {
const data = reactive({
username: "",
email: "",
password: "",
password2: "",
});
const validate = reactive({
email: "",
username: "",
password: "",
password2: "",
});
const router = useRouter();
const submit = async () => {
const res = await fetch(`${process.env.VUE_APP_API_URL}/auth/register`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(data),
});
if (!res.ok) {
const errors = await res.json().then((response) => response.errors);
for (const err of errors) {
const param: string = err.param ?? "";
validate[param] = err.msg;
}
}
};
return {
data,
validate,
submit,
};
},
});
</script>
as you can see I have defined and also exposed the validate property, which contains all the fields of the form.
When the API call is executed on the form submit, I reiceve this response if the backend validation fails:
{
"errors": [
{
"value": "",
"msg": "username must be at least 4 characters long",
"param": "username",
"location": "body"
},
{
"value": "test#",
"msg": "password confirm is different",
"param": "password2",
"location": "body"
}
]
}
I binded the validate property to each input field, so if the value entered in a specific field is incorrect, an error will be appended near the field and also the is-invalid class of Bootstrap is applied.
The errors variable contains the response above, what I'm trying to do is assign to each property of validate (which are the same name of the fields), the error messages, and I did:
const param: string = err.param ?? "";
validate[param] = err.msg;
the problem's that I get:
Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{ email: string; username: string; password: string; password2: string; }'.
No index signature with a parameter of type 'string' was found on type '{ email: string; username: string; password: string; password2: string; }'.
How can I fix this situation? And also, is there a better way to handle such scenario? 'cause I'm new to Vue and most probably I'm overcomplicating the situation here.
Kind regards
If you decided to add TypeScript to your project, it would be a good idea to start using it as intended, so let's firstly make a type for your error object:
type MyTypedError = {
value: string,
msg: string,
// the question mark indicates that the "param" property may not exists
// "typeof" infers the type of the "validate" variable (object in your case)
// "keyof" grabs all the property names from "validate":
param?: keyof typeof validate,
location: string
}
Let's make use of it. Now the TypeScript compiler will know the type of the received data.
if (!res.ok) {
// specify the type of the "errors" variable:
// MyTypedError[] (array of MyTypedError objects)
const errors: MyTypedError[] = await res.json().then((response) => response.errors);
...
}
The problem with your code: you are only guaranteeing that the param variable is type of string, but you don't promise the compiler that param holds any of the property names from validate. But since the compiler knows the type of err, it will even guide you how to make a working loop:
for (const err of errors) {
// We stated that 'param' property might not exist
if (err.param !== undefined) validate[err.param] = err.msg
}
EDIT: The assumption that err.param can also be undefined came from your code: err.param ?? "", though I don't see any reason why would it be.
i have one registration page when user renter some values ,after successfully submission the form need to be clear all the field for that i am using predefined function called reset() inside the script section ,it's not working i am unable to get where did i mistake and one more thing autocomplete="off" is also not working please help me to fix this issue.
Register.vue
<template>
<div class="main">
<div class="container">
<img src="../assets/sideImg.png" alt="notFound" />
<p>Online Book Shopping</p>
<div class="box">
<div class="headings">
<h5 class="signin">Login</h5>
<h5 class="signup">signup</h5>
</div>
<form id="myForm" #submit.prevent="handlesubmit">
<div class="fullname">
<p>FullName</p>
<input type="text" class="namebox" required v-model="FullName" autocomplete="off" pattern="[A-Za-z]{3,12}">
</div>
<div class="username">
<p>EmailID</p>
<input type="email" class="emailbox" required v-model="EmailID" pattern="^[a-z0-9._%+-]+#[a-z0-9.-]+\.[a-z]{2,4}$">
</div>
<div class="pass">
<p>Password</p>
<input :type="password_type" class="password" v-model="Password" pattern="^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[#$!%*?&])[A-Za-z\d#$!%*?&]{6,}$" required>
<i class="bi bi-eye-slash" id="togglePassword"></i>
</div>
<div class="mobile">
<p>MobileNumber</p>
<input type="tel" class="telephone" v-model="Mobile" pattern="^\d{10}$" required>
</div>
<button class="btn-section" #click="clear();" type="submit">Signup</button>
</form>
</div>
</div>
</div>
</template>
<script>
import service from '../service/User'
export default {
name: 'Register',
data() {
return {
FullName: '',
EmailID: '',
Password: '',
Mobile: '',
password_type: "password",
}
},
methods: {
togglePassword() {
this.password_type = this.password_type === 'password' ? 'text' : 'password'
},
clear(){
document.getElementById("myForm").reset();
},
handlesubmit() {
let userData = {
FullName: this.FullName,
EmailID: this.EmailID,
Password: this.Password,
Mobile: this.Mobile
}
service.userRegister(userData).then(response => {
// console.log("user Details", response);
alert("user registered successfully");
return response;
}).catch(error => {
alert("Invalid Email/Mobile number");
return error;
})
}
}
}
</script>
<style lang="scss" scoped>
#import "#/styles/Register.scss";
</style>
try this:
in your HTML
<form ref="myForm" #submit.prevent="handlesubmit">
in your handlesubmit, after it finishes
this.$refs.myForm.reset();
about autocomplete, found this:
In order to provide autocompletion, user-agents might require elements to:
Have a name and/or id attribute
Be descendants of a element
The form to have a submit button
try adding a name or id to the input.
I'm doing an user authentication using JWT auth in a SPA with Vue/Laravel. I have an issue with the register module, it isn't doing anything when I click the button, I checked Firefox developer edition's console and it throws me the following error:
TypeError: res.response is undefined
This is my code
<template>
<div class="container">
<div class="card card-default">
<div class="card-header">Inscription</div>
<div class="card-body">
<div class="alert alert-danger" v-if="has_error && !success">
<p v-if="error == 'registration_validation_error'">Error</p>
<p v-else>Try again later.</p>
</div>
<form autocomplete="off" #submit.prevent="register" v-if="!success" method="post">
<div class="form-group" v-bind:class="{ 'has-error': has_error && errors.email }">
<label for="email">E-mail</label>
<input type="email" id="email" class="form-control" placeholder="user#example.com" v-model="email">
<span class="help-block" v-if="has_error && errors.email">{{ errors.email }}</span>
</div>
<div class="form-group" v-bind:class="{ 'has-error': has_error && errors.password }">
<label for="password">Mot de passe</label>
<input type="password" id="password" class="form-control" v-model="password">
<span class="help-block" v-if="has_error && errors.password">{{ errors.password }}</span>
</div>
<div class="form-group" v-bind:class="{ 'has-error': has_error && errors.password }">
<label for="password_confirmation">Confirm password</label>
<input type="password" id="password_confirmation" class="form-control" v-model="password_confirmation">
</div> <button type="submit" class="btn btn-default" #click="register">Inscription</button>
</form>
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
name: '',
email: '',
password: '',
password_confirmation: '',
has_error: false,
error: '',
errors: {},
success: false
}
},
methods: {
register() {
var app = this
this.$auth.register({
data: {
email: app.email,
password: app.password,
password_confirmation: app.password_confirmation
},
success: function () {
app.success = true
this.$router.push({
name: 'Login',
params: {
successRegistrationRedirect: true
}
})
},
error: function (res) {
console.log(res.response.data.errors)
app.has_error = true
app.error = res.response.data.error
app.errors = res.response.data.errors || {}
}
})
}
}
}
</script>
If this gives some sort of hint I also have this warning in console:
Reason: CORS request did not succeed
I have no idea why is this happening.
You're running into a CORS error which means that the server you're making a request to is not allowing requests from your client domain. If you own the server you'll need to add your domain to the list of allowed domains, or if this fails, check that the request method "POST" is allowed.
It looks like you're then getting the 'res.response" error as when the error gets caught in the error function, it's returning an error message which doesn't have a "response" property. Try console.log'ing the "res" response to see what it's giving you and adjust the property you're using from there.
I'm building an app which uses vue.js and Laravel 5 to persist data into a database. So far, it can successfully save data.
My problem
I'm having trouble with displaying errors when the user validation fails using the 'out of the box' authorisation that Laravel has. I keep getting the error:
[Vue warn]: Error when evaluating expression "form.errors.length > 0"
(referring to errors.js in my code below)
Piecing it together
So how this should work...a user enters their details into the registration page and Vue (using vue-resource) makes an AJAX request to the 'out of the box' Laravel AuthController#postRegister method. On error, Laravel nicely spits out the JSON error messages we all expect. Now in theory, the sendRegistration method within my subscription.js file should detect the errors that Laravel spits out (I've tested this with console.log's and it works) and pass them into the errors.js component to display the errors within the <spark-errors> tags. In order to do this, it uses a setErrorsOnForm: function within my <head> tag. However it's not working as expected and I cannot workout why.
My code
My code consists of a registration page:
<!DOCTYPE html>
<html lang="en">
<head>
<!-- Laravel Spark Globals -->
<script>
window.Spark = {
// Laravel CSRF Token
csrfToken: '{{ csrf_token() }}',
// Flatten errors and set them on the given form
setErrorsOnForm: function (form, errors) {
if (typeof errors === 'object') {
form.errors = _.flatten(_.toArray(errors));
} else {
form.errors.push('Something went wrong. Please try again.');
}
}
}
</script>
</head>
<body>
<div id="spark-app">
<spark-subscription-register-screen inline-template>
<div class="panel panel-default">
<div class="panel-heading">Your Information</div>
<div class="panel-body">
<spark-errors form="#'{'{ registerForm '}'}"></spark-errors>
<form class="form-horizontal" role="form" id="subscription-basics-form">
<div class="form-group">
<label class="col-md-4 control-label">Your Name</label>
<div class="col-md-6">
<input type="text" class="form-control spark-first-field" name="name" v-model="registerForm.name">
</div>
</div>
<div class="form-group">
<label class="col-md-4 control-label">E-Mail Address</label>
<div class="col-md-6">
<input type="email" class="form-control" name="email" v-model="registerForm.email">
</div>
</div>
<div class="form-group">
<label class="col-md-4 control-label">Password</label>
<div class="col-md-6">
<input type="password" class="form-control" name="password" v-model="registerForm.password">
</div>
</div>
<div class="form-group">
<label class="col-md-4 control-label">Confirm Password</label>
<div class="col-md-6">
<input type="password" class="form-control" name="password_confirmation" v-model="registerForm.password_confirmation">
</div>
</div>
<div v-if="freePlanIsSelected">
<div class="form-group">
<div class="col-sm-6 col-sm-offset-4">
<div class="checkbox">
<label>
<input type="checkbox" v-model="registerForm.terms"> I Accept The Terms Of Service
</label>
</div>
</div>
</div>
<div class="form-group">
<div class="col-sm-6 col-sm-offset-4">
<button type="submit" class="btn btn-primary" v-on:click="register" :disabled="registerForm.registering">
<span v-if="registerForm.registering">
<i class="fa fa-btn fa-spinner fa-spin"></i> Registering
</span>
<span v-if=" ! registerForm.registering">
<i class="fa fa-btn fa-check-circle"></i> Register
</span>
</button>
</div>
</div>
</div>
</form>
</div>
</div>
</spark-subscription-register-screen inline-template>
</div>
</body>
</html>
From a vue.js perspective there is a subscription.js file:
Vue.component('spark-subscription-register-screen', {
/*
* Initial state of the component's data.
*/
data: function () {
return {
registerForm: {
nhs_org: '', team_name: '', name: '', email: '', password: '', password_confirmation: '',
plan: '', terms: false, coupon: null, invitation: null,
stripe_token: null, errors: [], registering: false
},
};
},
methods: {
/*
* Initialize the registration process.
*/
register: function(e) {
var self = this;
e.preventDefault();
this.registerForm.errors = [];
this.registerForm.registering = true;
return this.sendRegistration();
},
/*
* After obtaining the Stripe token, send the registration to Spark.
*/
sendRegistration: function() {
this.$http.post('/register', this.registerForm)
.success(function(response) {
window.location = '/';
})
.error(function(errors) {
this.registerForm.registering = false;
Spark.setErrorsOnForm(this.registerForm, errors);
});
},
}
});
and there is an errors.js Vue component:
/*
* Common Error Display Component.
*/
Vue.component('spark-errors', {
props: ['form'],
template: "<div><div class='alert alert-danger' v-if='form.errors.length > 0'>\
<strong>Whoops!</strong> There were some problems with your input.<br><br>\
<ul>\
<li v-for='error in form.errors'>\
{{ error }}\
</li>\
</ul>\
</div></div>"
});
Now the Vue files which piece all of this together (in the correct order):
app.js:
require('./core/dependencies');
if ($('#spark-app').length > 0) {
new Vue(require('./core/spark.js'));
}
core/dependencies.js:
/*
* Load Vue & Vue-Resource.
*
*/
if (window.Vue === undefined) window.Vue = require('vue');
require('vue-resource');
Vue.http.headers.common['X-CSRF-TOKEN'] = Spark.csrfToken;
/*
* Load Underscore.js, used for map / reduce on arrays.
*/
if (window._ === undefined) window._ = require('underscore');
/*
* Load jQuery and Bootstrap jQuery, used for front-end interaction.
*/
if (window.$ === undefined || window.jQuery === undefined) window.$ = window.jQuery = require('jquery');
require('bootstrap-sass/assets/javascripts/bootstrap');
core/spark.js:
/*
* Load the Spark components.
*/
require('./components');
/**
* Export the Spark application.
*/
module.exports = {
el: '#spark-app',
/*
* Bootstrap the application. Load the initial data.
*/
ready: function () {
$(function() {
$('.spark-first-field').filter(':visible:first').focus();
});
}
}
and core/components.js:
require('./../auth/registration/subscription');
require('./../common/errors');
I had the same issue, and instead of using the out of the box errors from spark, I defined my own component:
<template>
<div v-if="errors">
<div class="alert alert-danger" v-if="formattedErrors.length > 0">
<strong>Whoops!</strong> There were some problems with your input.<br><br>
<ul>
<li v-for="error in formattedErrors">
{{ error }}
</li>
</ul>
</div>
</div>
</template>
<script>
export default {
props: ['errors'],
computed: {
formattedErrors() {
return this.format(this.errors);
}
},
methods: {
format(errors) {
if (typeof errors === 'undefined') {
return [];
}
if (typeof errors === 'object') {
return _.flatten(_.toArray(errors));
}
}
}
}
</script>
Then you bind in your html:
<form-errors :errors="registerForm.errors"></form-errors>
It makes sense because your component just need the to now about errors, and nothing else about the registerForm.
Hope this helps.