What do you have ?
My frontend is made on Nuxt js and backend is made in node and graphql.
I have the following code snippet from my controller to generate relevant error message:
createUser: async function ({ userInput }, req) {
const errors = []
if (!validator.isEmail(userInput.email)) {
errors.push({ message: 'Email is invalid' })
}
//other custom error code
if (errors.length > 0) {
error = new Error('Invalid input')
error.data = errors;
error.code = 422;
throw error
}
//other code if no error
}
And my frontend code to reach the controller method from the page (register):
methods: {
async onSubmit(e) {
e.preventDefault()
const graphqlQuery = { //query code goes here}
const res = await this.$axios.post('http://localhost:8080/graphql', graphqlQuery, {
headers: {
'Content-Type': 'application/json'
}
})
if (res.status === 200) {
this.error.mode = true
this.error.title = 'Success ! '
this.error.message = 'Account created successfully. Please login'
this.error.type = 'success'
}
if (res.errors && res.errors[0].status === 422) {
this.error.mode = true
this.error.title = 'Verification failed ! '
this.error.message = res.errors[0].message
this.error.type = 'danger'
}
if (res.errors) {
this.error.mode = true
this.error.message = 'User creation failed ! '
this.error.type = 'danger'
}
}
}
And custom error page:
<template>
<b-container>
<b-row>
<b-col class="error-wrapper">
<h1 class="error-code">
{{ statusCode }}
</h1>
<h4>
{{ message }}
</h4>
<b-link to="/" class="btn to-home">
Home
</b-link>
</b-col>
</b-row>
</b-container>
</template>
<script>
export default {
name: 'ErrorVue',
layout: 'default',
props: {
error: {
type: Object,
default: null
}
},
computed: {
statusCode () {
return (this.error && this.error.statusCode) || 500
},
message () {
return this.error.message || 'Error'
}
}
}
</script>
<style scoped>
</style>
What are you trying to do ?
I'm trying to handle and display the custom error code with in the page, with the above error handling code.
What is the issue ?
When I intentionally pass wring data to cause error. I'm being redirected to error page with status 500.
I'm not able to console log error in the page(register) but able to log in the error.vue page.
The error does contain the custom code and message. And I want to display them in page from where the method was initiated(register).
How do I display the custom message in the register.vue page ? Or does anyone have alternative method to approach the issue ?
Related
i'm using Vue.js and Element Ui libraries for my project. I have front-end based validation with some rules. But i also need to implement display errors (for current field) from backend. When the form is sent and backend returns error it looks like this:
[
{"message": "email address is invalid", "path": ["email"]},
{"message": "example error for password field", "path": ["password"]}
]
where path is field name based on my form field model.
I created some extra element that displays error from backend as you can see in my fiddle. But i would like to use vue element ui validation. So backend errors should be displayed the same way as front-end messages. I can't figure out how to do this.
Here is my fiddle: https://jsfiddle.net/ts4Lfxb6/
Form code looks like this:
<el-form :model="loginForm" :rules="rules" ref="loginForm" label-position="top">
<el-form-item label="Email" prop="email">
<el-input v-model="loginForm.email" :disabled="formProcessing" ref="loginInput"></el-input>
<p v-if="isErrorForField('email', errors)">{{ getErrorForField('email', errors) }}</p>
</el-form-item>
<el-form-item label="Password" prop="password">
<el-input v-model="loginForm.password" :disabled="formProcessing" type="password"></el-input>
<p v-if="isErrorForField('password', errors)">{{ getErrorForField('password', errors) }}</p>
</el-form-item>
<el-form-item>
<div class="submit-wrapper">
<el-button type="primary" #click="submit('loginForm')" :loading="formProcessing">Log in</el-button>
</div>
</el-form-item>
</el-form>
And Full component is here:
var Main = {
data() {
return {
loginForm: {
email: '',
password: ''
},
rules: {
email: { required: true, message: 'Required', trigger: 'change' },
password: { required: true, message: 'Required', trigger: 'change' }
},
formProcessing: false,
errors: []
}
},
methods: {
isErrorForField (field, errors) {
if (!errors && !errors.length) {
return false
}
let filtered = errors.filter(error => {
return error.path[0] === field
})
if (filtered.length) {
return filtered
}
},
getErrorForField (field, errors) {
if (!errors && !errors.length) {
return false
}
let filtered = errors.filter(error => {
return error.path[0] === field
})
if (filtered.length) {
return filtered[0].message
}
},
supportGlobalErrorMessage () {
this.errors.forEach(error => {
if (!error.path.length) {
this.$message({
message: error.message,
type: 'error'
})
}
})
},
submit (formName) {
this.$refs[formName].validate(valid => {
if (!valid) {
return false
}
this.formProcessing = true
// send data to backend
// error response looks like this:
let errors = [
{"message": "email address is invalid", "path": ["email"]},
{"message": "example error for password field", "path": ["password"]}
]
setTimeout(() => {
this.formProcessing = false
this.errors = errors
this.supportGlobalErrorMessage()
}, 500)
})
}
}
}
var Ctor = Vue.extend(Main)
new Ctor().$mount('#app')
Can someone help?
Made the following changes to your code:
var Main = {
data() {
return {
loginForm: {
email: '',
password: ''
},
rules: {
email: {
required: true,
//validator: this.customValidator,
//trigger: 'blur'
},
password: {
required: true,
//validator: this.customValidator,
//trigger: 'blur'
}
},
formProcessing: false,
errors: []
}
},
methods: {
customValidator(rule, value, callback) {
console.log(rule)
if (!value) {
callback(new Error('The field is required'))
}
let errors = [{
"message": "email address is invalid",
"path": ["email"]
},
{
"message": "example error for password field",
"path": ["password"]
}
]
setTimeout(() => {
this.errors = errors
if (this.isErrorForField(rule.fullField, this.errors)) {
callback(new Error(this.getErrorForField(rule.fullField, this.errors)))
}
callback()
}, 500)
},
isErrorForField(field, errors) {
if (!errors && !errors.length) {
return false
}
let filtered = errors.filter(error => {
return error.path[0] === field
})
if (filtered.length) {
return filtered
}
},
getErrorForField(field, errors) {
if (!errors && !errors.length) {
return false
}
let filtered = errors.filter(error => {
return error.path[0] === field
})
if (filtered.length) {
return filtered[0].message
}
},
supportGlobalErrorMessage() {
this.errors.forEach(error => {
if (!error.path.length) {
this.$message({
message: error.message,
type: 'error'
})
}
})
},
submit(formName) {
this.$refs[formName].validate(valid => {
if (!valid) {
return false
}
this.formProcessing = true
// send data to backend
// error response looks like this:
let errors = [{
"message": "email address is invalid",
"path": ["email"]
},
{
"message": "example error for password field",
"path": ["password"]
}
]
setTimeout(() => {
this.errors = errors
this.formProcessing = false
this.supportGlobalErrorMessage()
}, 500)
})
}
}
}
var Ctor = Vue.extend(Main)
new Ctor().$mount('#app')
#import url("//unpkg.com/element-ui#2.0.5/lib/theme-chalk/index.css");
<script src="//unpkg.com/vue/dist/vue.js"></script>
<script src="//unpkg.com/element-ui#2.0.5/lib/index.js"></script>
<div id="app">
<el-form :model="loginForm" :rules="rules" ref="loginForm" label-position="top">
<el-form-item label="Email" prop="email" :error="getErrorForField('email', errors)">
<el-input v-model="loginForm.email" :disabled="formProcessing" ref="loginInput"></el-input>
<!-- <p v-if="isErrorForField('email', errors)">{{ getErrorForField('email', errors) }}</p> -->
</el-form-item>
<el-form-item label="Password" prop="password" :error="getErrorForField('password', errors)">
<el-input v-model="loginForm.password" :disabled="formProcessing" type="password"></el-input>
<!-- <p v-if="isErrorForField('password', errors)">{{ getErrorForField('password', errors) }}</p> -->
</el-form-item>
<el-form-item>
<div class="submit-wrapper">
<el-button type="primary" #click="submit('loginForm')" :loading="formProcessing">Log in</el-button>
</div>
</el-form-item>
</el-form>
</div>
The validator property in the rules attribute can set a custom validation rule which receive three parameters (rule, value, callback).
rule: The validation rule in the source descriptor that corresponds to the field name being validated. It is always assigned a field property with the name of the field being validated.
value: The value of the source object property being validated.
callback: A callback function to invoke once validation is complete. It expects to be passed an array of Error instances to indicate validation failure. If the check is synchronous, you can directly return a false or Error or Error Array.
So, you can get the data from the backend and process it, and then display the error message through the callback.
I'm having trouble finding a way to do this. I want to display a mat-error on my input form if an http get request fails (status = 404).
I have a search form and every time a user searches for something that doesn't exist I want to display the mat-error telling the user that his search is not valid.
Here's my client side code:
rest.service.ts
private extractData(res: Response) {
let body = res;
return body || { };
}
getOrder(id): Observable<any> {
return this.http.get(endpoint + 'orders/' + id, httpOptions).pipe(
map(this.extractData));
}
getReturns(): Observable<any> {
return this.http.get(endpoint + 'returns', httpOptions).pipe(
map(this.extractData));
}
MyComponent.component.ts
Here I have the getReturnByOrderID function that checks if the response data is empty or not. If it isn't then I open a mat dialog that will lead me to some other part of my site, however, if it is empty then I'm supposed to warn the user that what he has searched for doesn't exist.
I'm also using the getErrorMessage() function to handle all the form errors.
private inputForm = new FormControl('', [Validators.pattern(/^\d+$/)]);
constructor(public rest: RestService, private route: ActivatedRoute, private router: Router, public dialog: MatDialog) { }
getReturnByOrderId(value) {
if (value.length > 0) {
this.rest.getOrder(value).subscribe((data: {}) => {
if (Object.entries(data).length !== 0) {
this.openDialog(data);
} else {
//if 404, do something here
}
});
} else {
this.rest.getReturns().subscribe((data: {}) => {
this.returns = data;
});
}
}
openDialog(el) {
const dialogRef = this.dialog.open(MyDialogComponent, {
width: '70%',
data: el
});
dialogRef.afterClosed().subscribe(result => {
console.log(`Dialog result: ${result}`);
});
}
getErrorMessage() {
return this.inputForm.hasError('pattern') ? 'Insert numeric value!' : '';
// if the get request fails I'd have a message here saying "The order you searched for doesn't exist!"
}
MyComponent.component.html
<div class="row">
<div class="col-2"></div>
<div class="col-8">
<mat-form-field class="search-form-field" appearance="outline">
<mat-label>Search</mat-label>
<input matInput class="search-input" placeholder="Search" [formControl]="inputForm" #searchInput>
<mat-error *ngIf="inputForm?.invalid">{{ getErrorMessage() }}</mat-error>
<mat-hint>Insert an order ID.</mat-hint>
</mat-form-field>
<span matSuffix>
<button mat-raised-button class="search-btn" color="accent" [disabled]="inputForm?.invalid" (click)="getReturnByOrderId(searchInput.value)"><i class="fa fa-search"></i></button>
</span>
</div>
<div class="col-2"></div>
</div>
I'm not sure if my server side code would be usefull, if anyone needs it I'll edit my question...
Any suggestions on how I can achieve this? Thanks for the help!
When the server returns 404 status code, in this case, your subscribe block won't execute. so your need to write error block where you can handle 404 error and set form as invalid like below.
getReturnByOrderId(value) {
if (value.length > 0) {
this.rest.getOrder(value).subscribe((data: {}) => {
if (Object.entries(data).length !== 0) {
this.openDialog(data);
} else {
// if data is empty show
this.inputForm.setErrors({ 'invalid': true });
}
}, error => {
//when 404 error
this.inputForm.setErrors({ 'invalid': true });
});
} else {
this.rest.getReturns().subscribe((data: {}) => {
this.returns = data;
});
}
}
Hope this will help!
I'm new to Vue js - the following is not updating:
<div id="error" class="col s12 red center">
<span v-if="seen">
Error fetching readings: {{ msg }}
</span>
</div>
Vue:
var error = new Vue({
el: '#error',
data: {
msg: '',
seen: false
},
methods: {
show: function(message) {
this.msg = message;
this.seen = true;
},
hide: function() {
this.seen = false;
}
}
});
Post fetch:
fetch( ... )
.then(...)
.catch(err => {
error.show( err );
loader.hide();
});
error.show() displays the previously hidden div, but displays:
Error fetching readings: {}
Why?
i created a CodeSandbox sample based upon your code, you need to have computed property to have the Vue reactivity
Sample can be found, check code in HelloWorld.vue in components folder
https://codesandbox.io/s/x2klzr59wo
<template>
<div id="error" class="col s12 red center">
{{ seen }}
<hr />
<span v-if="computedSeen"> Error fetching readings: {{ msg }} </span>
<hr />
<button #click="show('effe');">SHOW</button>
<button #click="hide();">HIDE</button>
</div>
</template>
<script>
export default {
name: "HelloWorld",
data() {
return {
msg: "",
seen: false
};
},
methods: {
show: function(message) {
this.msg = message;
this.seen = true;
},
hide: function() {
this.seen = false;
}
},
computed: {
computedSeen: function() {
// `this` points to the vm instance
return this.seen;
}
}
};
</script>
Oops, problem was err from the fetch is an object, and I should have used err.message.
In my code I had a console.log('Error: %s', err) which appears to format the err object into text. Which is what threw me :(
Sorry.
I'm working on a Laravel PHP site, and am getting an error when trying to add a user to a cell in a table
The error says:
An error has occurred adding your contact. If the problem persists, please contact us.
and is displayed in a red 'ribbon' that pops up just below the browser address bar for a few seconds when trying to select a new user from the drop down.
I have seen a couple of similar questions on SO, but can't see how any of the answers apply to what's going on here...
In the HTML, the table column whose cell value I am trying to update is done via a form that pops up in a dialog box when pressing the 'Edit' icon in the cell:
<div class="provTaxContacts__row">
<form [formGroup]="newContactForm" class="provTaxContacts__col provTaxContacts__new-contact">
<label>Add new contact</label>
<div class="provTaxContacts__new-contact-fields">
<input class="provTaxContacts__new-contact-field provTaxContacts__name" [class.error]="newContactFormErrors.contactFirstName" placeholder="First name" type="text" autocomplete="given-name" formControlName="contactFirstName" />
<input class="provTaxContacts__new-contact-field provTaxContacts__name" [class.error]="newContactFormErrors.contactLastName" placeholder="Last name" type="text" autocomplete="family-name" formControlName="contactLastName" />
<input class="provTaxContacts__new-contact-field provTaxContacts__email" [class.error]="newContactFormErrors.contactEmail" placeholder="Email address" type="email" autocomplete="email" formControlName="contactEmail" />
<button class="btn btn-primary provTaxContacts__new-contact-button" type="button" (click)="onNewContactAdd(taxpayer.accountId)">Add contact</button>
<div *ngIf="addContactLoading" class="spinner-loading"></div>
</div>
</form>
</div>
The onNewContactAdd() function that's called when pressing the 'Add Contact' button is defined in a Typescript file called tax-reminder.ts, and as well as handling what happens to the browser on the front-end, it also calls the function addUserToAccount() from user.service.ts. It is what's displaying the error in the browser, and is defined with:
onNewContactAdd(accountId: number) {
const firstName = this.newContactForm.get('contactFirstName').value;
const lastName = this.newContactForm.get('contactLastName').value;
const email = this.newContactForm.get('contactEmail').value;
// Reset error states
this.resetContactFormErrors();
// Check for form errors
if (!firstName || Validate.isEmpty(firstName) || !Validate.lettersAndSpaces(firstName)) {
this.newContactFormErrors.contactFirstName = true;
} else {
this.newContactFormErrors.contactFirstName = false;
}
if (!lastName || Validate.isEmpty(lastName) || !Validate.lettersAndSpaces(lastName)) {
this.newContactFormErrors.contactLastName = true;
} else {
this.newContactFormErrors.contactLastName = false;
}
if (Validate.isEmpty(email) || !Validate.emailRegex.test(email)) {
this.newContactFormErrors.contactEmail = true;
} else {
this.newContactFormErrors.contactEmail = false;
}
// If there are any errors at this stage, Don't add
if (this.newContactFormErrors.contactFirstName || this.newContactFormErrors.contactLastName || this.newContactFormErrors.contactEmail) {
return
}
// Reset errors, just in case there were previous erros that we now know have been resolved
this.resetContactFormErrors();
this.addContactLoading = true;
// If all is valid, send a request to create the new contact
this.userService.addUserToAccount([{firstName, lastName, email, role: 'FULL'}], 'FULL', accountId)
.subscribe(
(response: any) => {
this.addContactLoading = false;
// Reset the add contact form so that the user can add more
this.newContactForm.patchValue({
contactFirstName: '',
contactLastName: '',
contactEmail: '',
});
// If the new contact's email address is already in the on-page list do nothing
if (_.find(this.contacts[accountId], {email})) {
return;
} else {
// If the request is succcessful, add the new contact to the list of contacts
this.contacts[accountId].push({
accountId,
email,
firstName,
groupTag: 'FULL',
lastName,
provTaxManager: 0,
provTaxPaymentsContact: 0,
userId: response.userId,
//transactionContactId,
});
}
},
error => {
console.log("Error: " + error);
const message = new Message();
message.type = MessageType.ERROR;
message.message = 'An error has occurred adding your contact. If the problem persists please contact us.';
this.messagingService.emitMessage(message);
}
)
}
In the browser console, I can see the following output in the Network-> Preview tab:
array:9 [
"userId" => 9561
"title" => null
"firstName" => "Shane"
"lastName" => "Williams"
"workPhone" => null
"mobilePhone" => null
"email" => "shane#williams.com"
"userTypeId" => 3
"login" => array:3 [
"loginId" => 9449
"loginName" => "shane#williams.com"
"userId" => 9561
]
]
Which shows that the details I entered into the form have been collected, and a new user ID has been assigned.
That output is coming from a dd() I have in the addAccountUser() PHP function:
public function addAccountUser( AddAccountUsersRequest $request )
{
$users = $request->input('users');
$type = $request->input('type');
$accountId = $request->input('accountId');
$userType = $type == 'taxfirm-agent' ? UserType::where('userTypeTag', 'AGENT')->first() : UserType::where('userTypeTag', 'DIRECT')->first();
$messages = array();
$hasWarningMessages = false;
try
{
DB::beginTransaction();
foreach ($users as $userRaw)
{
$details = array(
'firstName' => $userRaw['firstName'],
'lastName' => $userRaw['lastName'],
'email' => $userRaw['email'],
'password' => uniqid(),
'userTypeId' => $userType->userTypeId,
'accountId' => (!empty($accountId)) ? $accountId : null
);
$propertyValues = array();
// Adding tax agent
if ($type == 'taxfirm-agent') {
$group = $userRaw['role'];
$rv = $this->addTaxfirmAgent($details, $group);
}
else if($type == 'taxfirm-direct') {
$rv = $this->addTaxfirmDirectContact($details);
}
else {
$group = $userRaw['role'];
$rv = $this->addTaxpayerDirectContact($details, $group);
}
DB::commit();
dd($rv['user']->toArray());
if ($rv['status'] !== 'SUCCESS') {
if (!isset($messages[$rv['status']])) {
$messages[$rv['status']] = array(
'message' => StatusMessage::getMessage($rv['status']),
'data' => [],
//dd($messages);
);
}
$messages[$rv['status']]['data'][] = [$userRaw['email'], ucfirst($userRaw['firstName']), ucfirst($userRaw['lastName'])];
//dd($messages); // success is true at this point, users are null
if (!$hasWarningMessages)
{
$hasWarningMessages = true;
}
}
}
}
catch(\Exception $e)
{
DB::rollback();
return response()->json(array(
'success' => false,
'exceptionCode' => $e->getCode(),
'exceptionMessage' => $e->getMessage().' - '.$e->getFile().' - '.$e->getLine(),
'userId' => $userId // Try returning the userId too...
), 400);
}
$outputMsg = array();
foreach ($messages as $value) {
$outputMsg[] = $value;
}
//dd($users);
return response()->json(array(
'success' => true,
'hasWarningMessages' => $hasWarningMessages,
'result' => $outputMsg,
//'users' => $rv['user']->user, /*ERF(18/09/2018 # 1630) Change to userId */
'userId' => $rv['user']->userId,
));
}
I don't fully understand how the JavaScript, PHP & HTTP are all interacting here, or why the PHP debug appears to be showing the new contact created successfully, and yet I still get the error in the browser.
Can anyone point me in the right direction here? Why is the contact seemingly created, and yet I get the error, as well as the contact not being displayed in the drop down box as I am expecting?
Edit
So, I think that the issue I'm having here is not to do with the PHP itself- as that function seems to be returning the correct information (the console output given when I added the line dd($rv['user']->toArray()) at the end of the function showed all of the details for the user I had just added correctly), but rather to do with the Angular that should be updating the front end, to display the new user in the drop down.
That function is defined as follows:
this.userService.addUserToAccount([{firstName, lastName, email, role: 'FULL'}], 'FULL', accountId)
.subscribe(
(response: any) => {
this.addContactLoading = false;
// Reset the add contact form so that the user can add more
this.newContactForm.patchValue({
contactFirstName: '',
contactLastName: '',
contactEmail: '',
});
// If the new contact's email address is already in the on-page list do nothing
if (_.find(this.contacts[accountId], {email})) {
return;
} else {
// If the request is succcessful, add the new contact to the list of contacts
this.contacts[accountId].push({
accountId,
email,
firstName,
groupTag: 'FULL',
lastName,
provTaxManager: 0,
provTaxPaymentsContact: 0,
userId: response.userId,
//transactionContactId,
});
}
},
error => {
console.log("Error: " + error);
const message = new Message();
message.type = MessageType.ERROR;
message.message = 'An error has occurred adding your contact. If the problem persists please contact us.';
this.messagingService.emitMessage(message);
}
)
I think I need to add a call to reload this page element at the end of the else statement in this function... How would I do that?
Edit
So, it seems that although the contact appears to be created, the HTTP response I'm getting is actually the error- as I can see the An error has occurred adding your contact. If the problem persists please contact us. message in the browser... Why is it that the HTTP response is failing? How can I resolve this?
I added a console.log() to display the error in the console, and it's showing the following output:
unparsable response
Error: SyntaxError: Unexpected token < in JSON at position 0
I don't understand where this error is coming from... any ideas?
I was having a lot of trouble figuring out why I wasn't able to have an event bus listener update one of my components.
Below, in one of my component files, my server data property wouldn't get properly updated when using a traditional function accepting the outside server argument:
<template>
<div class="col-xs-12 col-sm-6">
<p v-if="!server">Please select a server</p>
<p v-else>Server #{{ server.id }} selected, Status: {{ server.status }}</p>
</div>
</template>
<script>
import { serverBus } from '../../main.js';
export default {
data: function() {
return {
server: null
}
},
created() {
/* DOESNT WORK */
serverBus.$on('serverSelected', function(server) {
this.server = server;
});
}
}
</script>
However, once I change serverBus.$on to accept a fat arrow as its parameter receiving the server, it works and the <p v-else> gets properly triggered after the server is no longer null.
<template>
<div class="col-xs-12 col-sm-6">
<p v-if="!server">Please select a server</p>
<p v-else>Server #{{ server.id }} selected, Status: {{ server.status }}</p>
</div>
</template>
<script>
import { serverBus } from '../../main.js';
export default {
data: function() {
return {
server: null
}
},
created() {
/* WORKS */
serverBus.$on('serverSelected', (server) => {
this.server = server;
});
}
}
</script>
Do you know why this is? I even did a console.log inside of the created() in the one that wasn't working, and the server information was getting properly logged... the <p v-else> just wasn't responding to the data server property no longer being null.
Here's my $emit portion, if it helps:
<script>
import { serverBus } from '../../main.js';
export default {
props: ['server'],
methods: {
serverSelected() {
serverBus.$emit('serverSelected', this.server);
}
}
}
</script>
As #RoyJ says, arrow functions are anonymous and change the way this binds in functions. Using the es5 syntax function creates a new context for this.
Consider the following:
var obj = {
user: 'John',
roles: ['Admin', 'Manager'],
displayUserRoles: function () {
return this.roles.map(function (role) { // es5 function
return this.user + ' is a ' + role + '.'
})
}
}
obj.displayUserRoles()
// output would be
['undefined is a Admin.', 'undefined is a Manager.']
vs:
var obj = {
user: 'John',
roles: ['Admin', 'Manager'],
displayUserRoles: function () {
return this.roles.map(role => { // es6 arrow function
return this.user + ' is a ' + role + '.'
})
}
}
obj.displayUserRoles()
// output would be
['John is a Admin.', 'John is a Manager.']