$refs resetFields not a function AntDesign - javascript

I have a problem with reseting fields of my form.
I have a form. The user can adding another forms and another ...
If everything is OK, I would like to recording data in my DB and in my store AND reset all of inputs of my form. This is this last point I cannot do.
I have tried different solutions but It does not work.
This is my code :
<div v-for="(question, index) in questionsSignaletiques" :key="index" class="blockQuestion" >
<!--form to add questions : one form per question, each form have a different name in the ref -->
<a-form-model
layout="inline"
:ref="'p' + index"
>
<p>New question</p>
<a-alert v-if="question.error.here" type="error" :message="question.error.message" show-icon />
<a-form-model-item>
<a-input v-model="question.question.type" placeholder="Title">
</a-input>
</a-form-model-item>
<a-form-model-item>
<a-input v-model="question.question.item" placeholder="Item">
</a-input>
</a-form-model-item>
<br><br>
<a-form-model-item label="Question multiple" prop="delivery">
<a-switch v-model="question.question.multiple" />
</a-form-model-item>
<a-form-model-item label="Obligatoire" prop="delivery">
<a-switch v-model="question.question.obligatoire" />
</a-form-model-item>
<br><br>
<div class="blockChoices">
<div v-for="subrow in question.question.choices">
<a-form-model-item>
<a-input v-model="subrow.name" placeholder="Choix">
</a-input>
</a-form-model-item>
</div>
</div>
<a-button #click="addChoice(question)" type="secondary">Add a choice</a-button>
</a-form-model>
</div>
<div>
<a-button #click="addItem" type="secondary">Add a question</a-button>
</div>
<br>
<div>
<a-button #click="submit" type="primary">Send</a-button>
</div>
Javascript code :
data() {
return {
idStudy: this.$route.params.id,
aucuneQuestion:false,
questionsSignaletiques: [
{
"study":"/api/studies/" + this.$route.params.id,
"question":
{
type: "",
item: "",
multiple: false,
obligatoire: false,
inverse: false,
barometre: false,
originale: false,
signaletik: true,
choices: [{name:''}]
},
"error": {
message:"",
here:false
}
}
],
}
},
mounted() {
//retreive all the questions still recorded
axios
.get('http://127.0.0.1:8000/api/studies/' + this.idStudy + '/question_studies?question.signaletik=true')
.then((result)=>{
console.log(result.data["hydra:member"])
this.aucuneQuestion = result.data["hydra:member"].length === 0;
//on met les données dans le store
this.$store.commit("setListeQuestionsSignaletiques", result.data["hydra:member"])
})
.catch(err=>console.log(err))
},
methods: {
//Adding a question
addItem () {
this.questionsSignaletiques.push(
{
"study":"/api/studies/" + this.idStudy,
"question":
{
type: "",
item: "",
multiple: false,
obligatoire: false,
inverse: false,
barometre: false,
originale: false,
signaletik: true,
choices: [{name:''}]
},
"error": {
message:"",
here:false
}
}
)
},
//adding a choice
addChoice: function(choice) {
choice.question.choices.push({
name: ''
})
},
// Sending the forms
submit () {
//loop table to retrieve all questions and indexes if the user adding several questions
this.questionsSignaletiques.forEach((element,index) =>
{
const inputType = element.question.type
const inputItem = element.question.item
const inputChoice = element.question.choices
//loop on choices to see if empty one or not
for (const oneChoice of inputChoice)
{
if ((inputChoice.length == 1) ||(inputChoice.length == 2 && oneChoice.name == ""))
{
element.error.here=true
element.error.message = "You must have two choices at least"
return false; // stop here if error
}
else
{}
}// end loop of choices
// verify other fields
if (inputType == "" || inputItem =="")
{
element.error.here=true
element.error.message = "Title and item must not be empty"
}
else
{
// everything is ok we can record in db and store
//reset fields == does not work
this.$refs['p' + index][0].fields.resetField()
//this.$refs['p' + index][0].resetFields();
// adding questions in db one by one
/*
axios
.post('http://127.0.0.1:8000/api/question_studies', element)
.then((result)=>{
console.log(result)
//add in the state
this.$store.commit("addQuestionSignaletique", element)
})
.catch(error => {
console.log("ERRRR:: ",error);
});
*/
}
}) //end loop foreach
}
}
};
Thanks a lot for help
EDIT AFTER THE FIRST ANSWER
Ok I didn't know. So I changed my mind : I added a "show" in my "data" which is true at the beginning. If everything is ok, I save the question and set the show to false.
The problem now is that when I have a question that is OK and the other one that is not. When I correct the question that was not ok and save it, BOTH questions go into the STATE. So there is a duplicate in my state AND my DB... What can I do? This is the code :
I just added this in the HTML :
<div v-for="(question, index) in questionsSignaletiques" :key="index" >
<a-form-model
layout="inline"
v-if="question.show"
class="blockQuestion"
>
...
data() {
return {
idStudy: this.$route.params.id,
aucuneQuestion:false,
questionsSignaletiques: [
{
"study":"/api/studies/" + this.$route.params.id,
"question":
{
type: "",
item: "",
multiple: false,
obligatoire: false,
inverse: false,
barometre: false,
originale: false,
signaletik: true,
choices: [{name:''}]
},
"error": {
message:"",
here:false
},
"show":true,
}
],
}
},
mounted() {
//retrieve question still recorded
axios
.get('http://127.0.0.1:8000/api/studies/' + this.idStudy + '/question_studies?question.signaletik=true')
.then((result)=>{
console.log(result.data["hydra:member"])
this.aucuneQuestion = result.data["hydra:member"].length === 0;
this.$store.commit("setListeQuestionsSignaletiques", result.data["hydra:member"])
})
.catch(err=>console.log(err))
},
methods: {
//adding a question
addItem () {
this.questionsSignaletiques.push(
{
"study":"/api/studies/" + this.idStudy,
"question":
{
type: "",
item: "",
multiple: false,
obligatoire: false,
inverse: false,
barometre: false,
originale: false,
signaletik: true,
choices: [{name:''}]
},
"error": {
message:"",
here:false
},
"show":true
}
)
},
//adding a choice
addChoice: function(choice) {
choice.question.choices.push({
name: ''
})
},
// submit the form
submit () {
this.questionsSignaletiques.forEach((element,index) =>
{
const inputType = element.question.type
const inputItem = element.question.item
const inputChoice = element.question.choices
for (const oneChoice of inputChoice)
{
if ((inputChoice.length == 1) ||(inputChoice.length == 2 && oneChoice.name == ""))
{
element.error.here=true
element.error.message = "You must have two choices at least"
return false; //on s'arrête là si il y a une erreur
}
else
{
console.log("no problem")
}
}
if (inputType == "" || inputItem =="")
{
element.error.here=true
element.error.message = "You must fill type and item"
}
else
{
// hide the question form
element.show =false
//adding in db
axios
.post('http://127.0.0.1:8000/api/question_studies', element)
.then((result)=>{
//add in the state
this.$store.commit("addQuestionSignaletique", element)
})
.catch(error => {
console.log("ERRRR:: ",error);
});
}
}) //end loop foreach
}
}
};
Thanks for help !

form.reset() does not work when using v-model.
Reset the reactive data instead.
reset() {
this.question.question.type = ""
...
...
}

Related

Cross Field Validation

Hoping I've provided enough info : Using the below links is anyone able to reproduce making Cross Field Validation form into one field that has a customer Your code must be 4 digits. & no error once 4 digit pin is entered (only any 4 numbers eg.(1234),(0000),(9999) (not any other characters))
https://codesandbox.io/s/s023f
https://vee-validate.logaretm.com/v3/advanced/rules-object-expression.html#cross-field-validation
Below is my Attempt with import { extend } from "vee-validate"; example using regex /^\d{4}$/ on equalFourDigits but I'm still seeing the custom message when 4 numbers are entered - I want to keep the custom message.
<script>
import { extend } from "vee-validate";
extend("equalFourDigits", {
params: ["equalFourDigits"],
validate: ({ equalFourDigits }) => {
if ( equalFourDigits === /^\d{4}$/ ) {
return true;
}
return false;
},
message:
"Your code must be 4 digits. {equalFourDigits}"
});
export default {
data: () => ({
firstValue: '',
secondValue: '',
equalFourDigits: ''
})
};
</script>
<template>
<ValidationObserver>
<ValidationProvider :rules="{ required: true, numeric: 4, equalFourDigits: { equalFourDigits:
equalFourDigits }}" v-slot="{ errors }">
<input type="text" v-model.number="secondValue">
<span>{{ errors[0] }}</span>
</ValidationProvider>
</ValidationObserver>
</template>
maybe you can just use :rules="{required: true, digits:4}" instead?
import {digits} from 'vee-validate/dist/rules';
extend('digits', {
...digits,
message: `only four digits ...!!!!`,
})
or
extend("equalFourDigits", {
validate: (value) => {
const regex = new RegExp(/^\d{4}$/)
if (regex.test(value)) {
return true;
}
return `Your code must be 4 digits. ${value}`;
},
});

Vue.js Element UI form validation - display errors from backend

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.

Textbox unique value in Laravel,vuejs,Axios, JavaScript

I want to be displayed the toastr popup message when the user try to insert the duplicate value for BageCode in form the if anyone can help me it will be so kind of him.
I the rest form column validation is working the toaster is working but I don't know how to compare the data from database and data in form
like this
if (this.form.BadgeCode == data.BadgeCode) {
toast.fire({
type: "warning",
title: "The badge code has already been taken."
});
}
Code in EmployeeController is:
public function index()
{
$date=\Carbon\Carbon::today();
// $this->authorize('isAdmin');
$employee=Employee::where('expiredate','>',$date)->paginate(5);
// return Employee::all();
return response()->json($employee);
}
public function store(Request $request)
{
// $this->authorize('isAdmin');
$this->validate($request,[
// 'BadgeCode'=>'required|string',
'BadgeCode'=>'required|unique:employees,BadgeCode',
'BadgeType'=>'required',
]) 'company_id'=>'required',
}
Code in api route is:
Route::apiResources(['employee'=>'API\EmployeeController']);
Code in Employee.vue is:
<div
style="width:45%;margin-left: 35%;"
class="modal fade"
id="addNew"
tabindex="-1"
role="dialog"
aria-labelledby="addNewLabel"
aria-hidden="true"
>
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" v-show="!editMode" id="addNewLabel">Add New employee</h5>
<h5 class="modal-title" v-show="editMode" id="addNewLabel">Update employee's Info</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<form
#submit.prevent="editMode ? updateemployee() : createemployee()"
enctype="multipart/form-data"
>
<div class="modal-body">
<div class="form-group">
<select
name="company_id"
id="company_id"
:class="{'is-invalid':form.errors.has('company_id')}"
class="form-control"
v-model="form.company_id"
>
<option value selected>Select Company</option>
<option
v-for="Company in Companies"
:key="Company.id"
:value="Company.id"
>{{Company.Company}}</option>
</select>
<has-error :form="form" field="company_id"></has-error>
</div>
<div class="form-group">
<input
v-model="form.BadgeCode"
placeholder="Enter BadgeCode"
type="text"
name="BadgeCode"
class="form-control"
/>
<!-- :class="{ 'is-invalid': form.errors.has('BadgeCode') }" -->
<!-- <has-error :form="form" field="BadgeCode"></has-error> -->
</div>
<div class="form-group">
<select
name="BadgeType"
v-model="form.BadgeType"
id="BadgeType"
class="form-control"
:class="{'is-invalid':form.errors.has('BadgeType')}"
>
<option value selected>Select BadgeType</option>
<option value="Resident">Resident</option>
<option value="Non-Resident-Expat">Non-Resident Expat</option>
<option value="Non-Resident-LN">Non-Resident LN</option>
<option value="Baron-employee">Baron-employee</option>
</select>
<has-error :form="form" field="BadgeType"></has-error>
</div>
</div>
</form>
</div>
</div>
</div>
Code in script is:
<script>
export default {
// el: "#wrapper2",
data() {
return {
seen: false,
seen2: true,
color: "blue",
size: "20px",
editMode: false,
Companies: {},
employees: {},
form: new Form({
id: "",
BadgeCode: "",
BadgeType: "",
company_id: "",
})
};
},
methods: {
getvalidateion() {
axios.get("api/employee").then(response => {
let data = response.data.data;
if (data) {
data.forEach(element => {
if (this.form.BadgeCode == data.BadgeCode) {
toast.fire({
type: "warning",
title: "The badge code has already been taken."
});
}
});
} else {
console.log("NO DATA");
}
});
},
createemployee() {
this.getvalidateion();
if (this.form.company_id == "") {
toast.fire({
type: "warning",
title: "Please Select the Company."
});
} else if (this.form.BadgeCode == "") {
toast.fire({
type: "warning",
title: "BadgeCode is required."
});
} else if (this.form.BadgeType == "") {
toast.fire({
type: "warning",
title: "Please Select the BadgeType."
});
} else if (this.form.nationality_id == "") {
toast.fire({
type: "warning",
title: "Please Select the Nationality."
});
} else if (this.form.expiredate == "" || this.form.issuedate == "") {
toast.fire({
type: "warning",
title: "Please fill the issuedate or expiredate fileds ."
});
} else if (this.form.photo.length == "") {
toast.fire({
type: "warning",
title: "Please select photo."
});
} else if (
this.form.afghanidatephoto != undefined &&
this.form.afghanidatephoto.length == ""
) {
toast.fire({
type: "warning",
title: "Please select AfghaniDate."
});
} else {
this.form
.post("api/employee")
.then(() => {
// the below function will be use to reload the page
// this.$emit("refreshPage");
$("#addNew").modal("hide");
toast.fire({
type: "success",
title: "employee Created successfully"
});
this.form.reset();
$("#afghanidatephoto").val("");
$("#photo").val("");
Fire.$emit("refreshPage");
})
.catch(e => {
console.log(e);
});
}
},
created() {
Fire.$on("searching", () => {
let query = this.$parent.search;
axios
.get("api/findemployee?q=" + query)
.then(data => {
this.employees = data.data;
})
.catch(() => {});
});
this.loademployees();
this.getvalidateion();
// load the page after 3 secound
Fire.$on("refreshPage", () => {
this.loademployees();
});
}
};
</script>
Code in Modal is:
$table->string('BadgeCode')->unique();
I found the solution instate of data.BadgeCode I should have put element.BadgeCode anyway thanks guys
getvalidateion() {
axios.get(this.url).then(response => {
let data = response.data;
//this.Companies != undefined && this.Companies.length == 0
if (data) {
data.forEach(element => {
this.employees2.push(element.BadgeCode);
if (this.form.BadgeCode == element.BadgeCode) {
toast.fire({
type: "warning",
title: "BadgeCode is already has been taken."
});
}
});
} else {
console.log("NO DATA");
}
});
}

Model and Computed Property interaction in Vue.js

Using vue.js I am trying to build a simple task manager.
When a user clicks the "complete" checkbox I want two things to happen:
If the "Show all tasks" is unchecked, hide the task.
Send an ajax request to the server to mark the task as complete/open.
The impotent parts are shown below:
<div id="tasks-app">
<input type="checkbox" id="checkbox" v-model="show_all">
<label for="checkbox">Show all tasks</label><br>
<table class="table">
<tr><th v-for="column in table_columns" v-text="column"></th><tr>
<tr v-for="row in visibleTasks" :class="{danger: !row.daily_task.complete && row.daily_task.delayed, success: row.daily_task.complete}">
<td v-text="row.task.name"></td>
<td v-text="row.task.deadline"></td>
<td v-text="row.daily_task.status"></td>
<td v-text="row.daily_task.task_user"></td>
<td>
<input type="checkbox" v-on:change="updateStatus(row)" v-model="row.daily_task.complete" >Complete</input>
</td>
<td><input v-model="row.daily_task.delay_reason"></input></td>
</table>
</div>
And the VUE.js code:
app = new Vue({
el: '#tasks-app',
data: {
table_columns: ['Task','Deadline','Status','User','Actions','Reason'],
tasks: [],
filter_string: '',
show_all: false
},
computed: {
visibleTasks() {
show_all = this.show_all
if(show_all){
search_filter = this.tasks
}else{
search_filter = _.filter(this.tasks,function(task){
return !task.daily_task.complete;
})
}
return search_filter
}
},
methods: {
updateStatus(row){
var id = row.daily_task.id
var complete = row.daily_task.complete
if(complete){
axios.get('set_task_complete/' + id)
}else{
axios.get('set_task_open/' + id)
}
}
}
})
If the show all checkbox is checked, this works as expected. The data changes and then the updateStatus function is called.
If however the show all checkbox is unchecked, the visibleTasks will trigger and the logic for the updateStatus will fail, as the row will be hidden and the ID that is send to the server will be off by one. If the row hides before updateStatusis called the wrong row is passed to the updateStatus function.
I could solve this by adding a filter update at the end of updateStatus function but that does not seems to utilize the Vue.js library. Could someone help me what components of Vue you would use to solve this problem?
You can simplify a lot if you separate the logic:
Loop over every tasks (completed or not) to display them (without considering show_all)
Update tasks with v-on:click="updateStatus(row)"
On each tr, add v-show="show_all || !row.status.complete"
Your problem is use both change event handle and model. Both actually, trigger at same time when you click on the checkbox.
<input type="checkbox" v-on:change="updateStatus(row)" v-model="row.daily_task.complete" >Complete</input>
You should edit your code use only v-on:change="updateStatus(row)". After updateStatus complete ajax calls toggle row.daily_task.complete to trigger visibleTasks to update your view.
updateStatus(row){
var id = row.daily_task.id
var complete = !row.daily_task.complete
var p;
if(complete){
p = axios.get('set_task_complete/' + id)
}else{
p = axios.get('set_task_open/' + id)
}
p.then(() => row.daily_task.complete = complete)
}
I've refactored your code a bit and it seems to work fine:
you shouldn't use v-on:change, instead, use <input type="checkbox" v-on:click="updateStatus(row)" v-bind:checked="row.daily_task.complete">
Don't update row.daily_task.complete straight away, update it only when the asynchronous axios is complete.
const fakeUpdateComplete = (id) => {
return new Promise((resolve, reject) => { resolve(true); });
};
const fakeUpdateIncomplete = (id) => {
return new Promise((resolve, reject) => { resolve(true); });
};
const app = new Vue({
el: '#tasks-app',
data: {
history: [],
table_columns: ['Task','Deadline','Status','User','Actions','Reason'],
tasks: [
{
task: {
name: 'A',
deadline: '2017-01-01',
},
daily_task: {
id: 1,
status: '',
task_user: '',
complete: true,
delayed: false,
delay_reason: ''
}
},
{
task: {
name: 'B',
deadline: '2017-01-02',
},
daily_task: {
id: 2,
status: '',
task_user: '',
complete: false,
delayed: false,
delay_reason: ''
}
},
{
task: {
name: 'C',
deadline: '2017-01-03',
},
daily_task: {
id: 3,
status: '',
task_user: '',
complete: false,
delayed: false,
delay_reason: ''
}
},
{
task: {
name: 'D',
deadline: '2017-01-03',
},
daily_task: {
id: 4,
status: '',
task_user: '',
complete: true,
delayed: false,
delay_reason: ''
}
},
{
task: {
name: 'E',
deadline: '2017-01-03',
},
daily_task: {
id: 5,
status: '',
task_user: '',
complete: false,
delayed: false,
delay_reason: ''
}
}
],
filter_string: '',
show_all: true
},
computed: {
visibleTasks() {
const show_all = this.show_all
let search_filter;
if(show_all){
search_filter = this.tasks
}else{
console.log(this.tasks);
search_filter = this.tasks.filter(task => {
return !task.daily_task.complete
});
}
return search_filter
}
},
methods: {
updateStatus(row){
const id = row.daily_task.id;
const complete = !row.daily_task.complete;
if (complete) {
fakeUpdateComplete(id).then(() => {
this.history.push(`Task ${row.task.name} with id ${row.daily_task.id} is marked as complete`);
row.daily_task.complete = !row.daily_task.complete;
});
} else {
fakeUpdateIncomplete(id).then(() => {
this.history.push(`Task ${row.task.name} with id ${row.daily_task.id} is marked as incomplete`);
row.daily_task.complete = !row.daily_task.complete;
});
}
/*
if(complete){
axios.get('set_task_complete/' + id)
}else{
axios.get('set_task_open/' + id)
}
*/
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.4.2/vue.js"></script>
<div id="tasks-app">
<input type="checkbox" id="checkbox" v-model="show_all">
<label for="checkbox">Show all tasks</label><br>
<table class="table">
<tr><th v-for="column in table_columns" v-text="column"></th><tr>
<tr
v-for="row in visibleTasks"
:class="{danger: !row.daily_task.complete && row.daily_task.delayed, success: row.daily_task.complete}"
>
<td>{{row.task.name}}</td>
<td>{{row.task.deadline}}</td>
<td>{{row.daily_task.status}}</td>
<td>{{row.daily_task.task_user}}</td>
<td>
<input type="checkbox" v-on:click="updateStatus(row)" v-bind:checked="row.daily_task.complete">
<label for="complete">Complete</label>
</td>
<td><input v-model="row.daily_task.delay_reason" /></td>
</tr>
</table>
<div>
<div><b>Data:</b></div>
<div>{{this.tasks}}</div>
</div>
<div>
<div><b>History:</b></div>
<div v-for="item in history">
{{item}}
</div>
</div>
</div>

React doesn't update component state when user changes input

I'm using React with express to render the views of my project server-side but I came across a problem.
I have a component where the users go after loging in for the first time. There are two inputs to write the user's new password and to confirm it. I'm trying to validate that the password entered is 8 characters long at least and that the value of both inputs is the same.
React doesn't seems to update the component state and thus, my verification process doesn't work. Am I doing something wrong? Here's the code:
set-password.jsx
module.exports = React.createClass({
getInitialState: function() {
return {
sPassword: '',
validLength: false,
validPassword: false
}
},
checkLength: function(event) {
var pLength = event.target.value;
pLength.length >= 8 ?
this.setState({ validLength: true }) :
this.setState({ validLength: false });
},
checkPassword: function(event) {
event.target.value === this.state.sPassword ?
this.setState({ validPassword: true, sPassword: event.target.value}) :
this.setState({ validPassword: false });
},
render: function() {
return (
<Layout>
<Form action='/auth/setpassword' method='POST' name='setPassword'>
<h1>Welcome</h1>
<p>...</p>
<div className='row'>
<div className='col-sm-6'>
<Input
type='password'
label='Introduzca su nueva contraseña'
name='password'
onChange={this.checkLength}
value={this.state.sPassword}
/>
</div>
<div className='col-sm-6'>
<Input
type='password'
label='Escriba de nuevo su contraseña'
name='confirm_password'
onChange={this.checkPassword}
/>
</div>
</div>
<SubmitButton value='Guardar nueva contraseña' />
<div>
<p>Contraseña verificada: <span>{this.state.sPassword}</span></p>
</div>
</Form>
</Layout>
)
}
});
I'm using checkLength() on the first input to verify the password length. Then checkPassword() verifies that both inputs have the same code and then update this.state.sPassword to be the first input value, which will be sent to the system endpoint.
After the SubmitButton component I'm printing the value of this.state.sPassword to see if the state is changing, but it does not. Can you help me? Thanks.
Your issue is that you are not setting the value of sPassword properly from its own input, the only thing you are doing is setting it when the other input changes. Additionally, that code:
checkPassword: function(event) {
event.target.value === this.state.sPassword ?
this.setState({ validPassword: true, sPassword: event.target.value}) :
this.setState({ validPassword: false });
},
is redundant because you are setting sPassword to the same value it already has, assuming the true case passes. Basically you are saying if '123' == password then set password to '123'. Furthermore, that case will never happen because sPassword never gets updated from its own input and thus is always an empty string.
What you need to do is, instead of calling checkLength() in first inputs onChange, you call something like updateSPassword() to properly set the password. Further more you can do the same for the second input. Finally you can add a validate method that is called when either of the passwords change and that performs your validation:
module.exports = React.createClass({
getInitialState: function() {
return {
sPassword: '',
cPassword: '',
validLength: false,
validPassword: false
}
},
setSPasswordAndValidate: function(e) {
this.setState({
sPassword: e.target.value
});
this.validate();
},
setCPasswordAndValidate: function(e) {
this.setState({
cPassword: e.target.value
});
this.validate();
},
validate: function() {
var pw1 = this.state.sPassword;
var pw2 = this.state.cPassword;
var validPassword = pw1 === pw2;
var validLength = validPassword && pw1.length >= 8;
this.setState({
validPassword: validPassword,
validLength: validLength
});
}
render: function() {
// ...
<Input
type='password'
label='Introduzca su nueva contraseña'
name='password'
onChange={this.setSPasswordAndValidate}
value={this.state.sPassword}
/>
// ...
<Input
type='password'
label='Escriba de nuevo su contraseña'
name='confirm_password'
onChange={this.setCPasswordAndValidate}
value={this.state.cPassword}
/>
}
});
Or you can even combine the setting of passwords and validation into a single method:
setSPasswordAndValidate: function(e) {
this.validate(e.target.value, this.state.cPassword);
},
setCPasswordAndValidate: function(e) {
this.validate(this.state.sPassword, e.target.value);
},
validate: function(sPassword, cPassword) {
var validPassword = sPassword === cPassword;
var validLength = validPassword && sPassword.length >= 8;
this.setState({
sPassword: sPassword,
cPassword: cPassword,
validPassword: validPassword,
validLength: validLength
});
}

Categories