Vue displays validation text on multiple fields - javascript

There are two buttons called ADD and REMOVE. If the user clicks on ADD it will add one more input field for FULL NAME. I am using validationText to display text as PLEASE ENTER MORE THAN 5 CHARACTERS for full name. If I ADD two fields and insert only two characters in second one then it displays validationText on both input fields as
Is there a way to display validationText message to the particular field which consists of less than 5 characters?
View
<div id="app">
<div class="work-experiences">
<div class="form-row" v-for="(minordatabase, index) in minorsDetail" :key="index">
<div class="col">
<br>
<label id="minorHeading">FULL NAME</label>
<input v-model="minordatabase.full_name" type="text" class="form-control" placeholder="FULL NAME" size="lg" #input="checkValidation"/>
<p v-show="!validationText" style="color:red;">
Please enter than 5 characters
</p>
</div>
</div>
</div>
<br>
<div class="form-group">
<button #click="addExperience" type="button" class="btn btn-info" style="margin-right:1.5%;">Add</button>
<button #click="removeExperience" type="button" class="btn btn-outline-info">Remove Last Field</button>
</div>
</div>
Script
new Vue({
el: "#app",
data: {
minorsDetail: [
{
full_name: "",
date_of_birth: "",
}
],
validationText: true
},
methods: {
checkValidation(){
console.log("SAN");
var minorsDetailLastElement = this.minorsDetail[this.minorsDetail.length-1].full_name.length;
console.log(minorsDetailLastElement);
if(minorsDetailLastElement > 2){
this.validationText = false;
}
if(minorsDetailLastElement > 5){
this.validationText = true;
}
},
addExperience(){
this.minorsDetail.push({
full_name: ''
})
},
removeExperience: function(todo){
var index = this.minorsDetail.indexOf(todo)
this.minorsDetail.splice(index, 1)
this.removeMinorFieldFunction();
},
}
})
Below is the code on JSFIDDLE
https://jsfiddle.net/ujjumaki/5mqp1bag/28/

You only have one validationText for all fields. So, if you set it for one field, it's going to show up in the other field too.
I recommend doing something like this instead to show the validation:
<p v-if="minordatabase.full_name.length > 2 && minordatabase.full_name.length < 5" style="color: red;">
Please enter more than 5 characters
</p>

Related

userForm value not in range

I am trying to validate the length of ID value in userForm in Angular14, I tried for
if(this.userForm.value.id.length < 6 || this.userForm.value.id.length >9){
console.log("length error")
}
But this is not giving the expected result. the above snippet is working for != and = only.
How can I validate this value for the range.
Adding the .ts and .html snippets for the better understanding.
component.ts
userSubmit(){
console.log(this.userForm.get('image').value);
console.log(this.userForm.value);
const formData = new FormData();
formData.append('image', this.images);
formData.append('id', this.userForm.value.id);
formData.append('des', this.userForm.value.des);
formData.append('name', this.userForm.value.name);
if(this.userForm.value.id.length < 6 || this.userForm.value.id.length > 9){
console.log("length error")
}
component.html
<div class="container">
<div *ngIf="errorMsg" class="alert alert-danger alert-dismissible fade show" role="alert">
<strong>{{errorMsg}}</strong>
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
<form [formGroup]="userForm" >
<div class="col-lg-4 mt-2" >
<label>Employee ID:</label>
<input type="number" class="form-control" formControlName="id" [(ngModel)]="id">
</div>
<div class="col-lg-4 mt-2" *ngIf="!getParamId" >
<button class="btn btn-primary btn-sm" [disabled]="!name || !des ||!photo||!id" (click)="userSubmit()">
Submit!!
</button>
</div>
<div class="col-lg-4 mt-2" *ngIf="getParamId" >
<button class="btn btn-dark btn-sm" (click)="userUpdate()" >
Update
</button>
</div>
</form>
</div>
From what I understand from your question, you are performing the validation for id with the length within a range.
The problem is currently your id is not a string, but it is a number.
<input type="number" class="form-control" formControlName="id" [(ngModel)]="id">
Hence you can't use .length as this property doesn't exist in the number value.
Change the <input> element from type="number" to type="text" or without type. (By default type is text).
Since you are using the Reactive form, you don't need the [(ngModel)].
<input type="text" class="form-control" formControlName="id" />
Add Validators.minLength() and Validators.maxLength() validators to id form control.
import { Validators } from '#angular/forms';
this.userForm = this.fb.group({
id: [
'',
[Validators.required, Validators.minLength(6), Validators.maxLength(9)],
],
// Following controls
});
Note: If you want the id input to be numeric characters, you need Validators.pattern():
Validators.pattern('[0-9]+')
Or validate with the length as well, so the Validators.minLength() and Validators.maxLength() can be omitted.
Validators.pattern('[0-9]{6,9}')
To disable the submit button when there is any control(s) that failed the validation:
<button
class="btn btn-primary btn-sm"
[disabled]="idHasError /* or other form controls */"
(click)="userSubmit()"
>
Submit!!
</button>
And implementing the getter.
get idHasError() {
return this.userForm.controls.id.errors;
}
Implement the getter method(s) for the rest form control(s) to return a boolean value that the form control has errors or not.
Demo # StackBlitz

How to append input elements dynamically in vue

I'm trying to append new input fields based on a condition, I will describe the workflow to help you understand
First stage is to press this button to implement 2 functions(1 is to move to other fieldset in the staged form, second function is to append the inputs:
<input type="button" name="secondBtn" class="next action-button" value="Next" id="secondBtn" #click="nextPlusappend"/>
nextPlusappend:
nextPlusappend() {
this.isNextClicked();
this.appendFields();
}
appendFields:
//this.platform initllized as 'one' so the condition is true.
if(this.platform === 'one'){
this.inputFields.push({
Username: '',
Password: '',
AffiliateID: '',
CI: '',
GI: '',
})
}
And I want to append all the fields from the function to this fieldset:
<div v-if="currentPage === 2">
<fieldset id="fieldset3" v-for="(param, index) in inputFields" :key="index">
<h2 class="fs-title">API Credentials</h2>
<h3 class="fs-subtitle">Step 3- Add any parameter for the integration</h3>
<input v-model="param.Username" type="text" name="`inputFields[${index}[Username]]`" placeholder="userName">
<input type="button" name="previous" class="previous action-button" value="Previous" #click="isPreviousClicked"/>
<input type="submit" name="submit" class="submit action-button" value="Create a Ticket" id="excel"/>
</fieldset>
</div>
How can I append this without hard code all the input fields as I did here:?
<input v-model="param.Username" type="text" name="`inputFields[${index}[Username]]`" placeholder="userName">
This is designated to be dynamic, what do i mean?
I mean that if the this.platform is equal to "one" there will be a unique fields, and if this.platform equal to "two" for example there will be other unique fields.
Don't think like "pushing a form field", rather think like "adding a new item to the dataset" (and of course, its displayed UI is a form field).
Let me give an example:
Vue.component("FormField", {
props: ["label", "value"],
computed: {
val: {
get() {
return this.value
},
set(val) {
this.$emit("update:value", val)
}
},
},
methods: {
handleClickAdd() {
this.$emit("click-add-field")
}
},
template: `
<div>
<label>
{{ label }}: <input type="text" v-model="val" />
</label>
<button
#click="handleClickAdd"
>
+ ADD
</button>
</div>
`,
})
new Vue({
el: "#app",
data() {
return {
formFields: [{
label: "Field 1",
value: null,
}],
}
},
methods: {
handleClickAddField() {
const item = {
label: `Field ${ this.formFields.length + 1 }`,
value: null,
}
this.formFields = [...this.formFields, item]
},
},
template: `
<div
class="container"
>
<div
class="col"
>
<h4>FIELDS:</h4>
<hr>
<form-field
v-for="(field, i) in formFields"
:key="i"
:label="field.label"
:value.sync="field.value"
#click-add-field="handleClickAddField"
/>
</div>
<div
class="col"
>
<h4>FIELD VALUES:</h4>
<hr>
<div
v-for="(field, i) in formFields"
:key="i"
>{{ field.label }}: {{ field.value }}</div>
</div>
</div>
`,
})
.container {
display: flex;
}
.col {
padding: 0 8px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app"></div>
You can see, that on ADD I just added a new item in the formFields - the values are bound in the template to a child-component, that handles the actual representation of the fields.
On the right side of the snippet, you can see another benefit of decoupling data from UI: I created another representation of the same data source - that immediately reacts to any changes!

Form validation in vue

I am trying to understand and work around to validate a simple form but the problem I am facing is, when the page loads, it shows the "Error" or "Success" message, which should display only either on keypress or mouseout event.
Moreover, I cannot figure out how to validate the dropdown and finally when a user click submit, it can check all fields are filled and correct to submit the form. Following is my code and my link to JSFiddle
HTML
<div id="app">
<div>
<label for="name">Name</label>
<input type="text" #keypress="checkField" v-model="name">
<span v-if="checkName">Checks out </span>
<span v-if="!checkName">Pleas enter valid name</span>
</div>
<div>
<label for="Age">Age</label>
<input type="number" #keypress="checkField2" v-model="age">
<span v-if="checkAge">Enter Valid Age </span>
<span v-if="!checkAge">Not a valid age</span>
</div>
<div>
<select name="" id="">
<option disabled selected>Please Choose</option>
<option v-for="gender in genders" :value="gender">
{{gender}}
</option>
</select>
<span v-if="genderField">Please select a gender</span>
<span v-if="!genderField">Green means go</span>
</div>
<div>
<button #click="checkSubmit(e)">Submit</button>
</div>
</div>
JS
data: {
name: "",
checkName: "",
age: "",
checkAge: "",
genders : ["Male",'Female',"Other"],
genderField: ""
},
methods: {
checkField() {
if (!this.amount) {
this.checkName = true
}
},
checkGender() {
if(!this.genders){
this.genderField = true
}
},
checkSubmit(e){
//check if all fields are filled before submitting
alert("it is working")
e.preventDefault()
}
}
})
There is a lot of ways to validate forms. I have a few tips for this kind of case.
Use a form element with #submit.prevent="..." event handler. It will ensure a better user experience;
Do not use #key* event handlers to validate or format a value, instead, use #input. It will prevent you from a lot of headache;
Vue provide a API to watch all the attribute changes, not only when the user changes it.
For solve your problem, you can create a validation attribute and set its content acording the other attributes change.
See the example below:
BTW: I recommend that you take a look on vuelidate
const app = new Vue({
data() {
return {
name: null,
age: null,
gender: null,
genders: ['Male', 'Female', "Other"],
validations: {}
}
},
methods: {
submit(e) {
const keys = Object.keys(this.validations);
// required fields
const required = ['name', 'age', 'gender'];
for (const key in required) {
if (!keys.includes(required[key])) {
alert('Please, insert a ' + required[key]);
return;
}
}
for (const key in this.validations) {
if (!this.validations[key]) {
alert('Please, insert valid ' + key);
return;
}
}
alert("ok");
}
},
watch: {
name(newValue) {
this.validations.name = newValue > '';
},
age(newValue) {
this.validations.age = newValue > 0;
},
gender(newValue) {
this.validations.gender = this.genders.includes(newValue);
}
}
});
app.$mount("#app");
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<form #submit.prevent="submit">
<div>
<label for="name">Name</label>
<input type="text" v-model="name">
<span v-if="'name' in validations && validations.name">Checks out </span>
<span v-if="'name' in validations && !validations.name">Pleas enter valid name</span>
</div>
<div>
<label for="age">Age</label>
<input type="number" v-model="age">
<span v-if="'age' in validations && !validations.age">Enter Valid Age</span>
</div>
<div>
<label for="gender">Gender</label>
<select name="gender" v-model="gender">
<option disabled selected>Please Choose</option>
<option v-for="gender in genders" :value="gender">
{{gender}}
</option>
</select>
<span v-if="'gender' in validations && validations.gender">Green means go</span>
<span v-if="'gender' in validations && !validations.gender">Please select a gender</span>
</div>
<input type="submit" value="Submit">
</form>
</div>
In Angular, we have a built-in option to validate forms. But Vue offers very limited functionality when it comes to create and validate forms. To add more functionality, we have to install a separate package called Vuelidate to validate our Vue application forms.
What is Vuelidate?
According to the Vuelidate website:
“Vuelidate 2 is a simple, but powerful, lightweight model-based validation for Vue.js 3 and Vue 2.x.”
Install
npm install #vuelidate/core #vuelidate/validators
Reference:
https://aaqibqs.medium.com/learn-form-validation-in-vue-3-in-10-minutes-with-vuelidate-8929c5059e66

How to make Semantic error message outside of form?

I have one problem. I have to make my error messages display outside of form, but I have no idea how to make it, because my javascript is displaying messages, when the error message class is inside in the form. is it possible to make it outside of form?
This is picture, how it looks. In this example I want to display errors below the red button.1
<form class="ui form" id="companyForm" style="margin-top:1em">
<div class="two fields">
<div class="field">
<input type="text" name="companyName" placeholder="Company name">
</div>
<div class="field">
<input type="text" name="companyCode" placeholder="Company code">
</div>
</div>
<div class="field">
<input type="text" name="companyAddress" placeholder="Company address">
</div>
<div class="ui error message"></div>
</form>
<div class="ui bottom attached red button" onclick="submitForm(this);">
<p><b>Place order</b> <i class="large shop icon"></i></p>
</div>
<div class="ui error message"></div>
function submitForm(){
var formValidationRules2 =
{
companyName: {
identifier: 'companyName',
rules: [
{
type : 'minLength[2]',
prompt : 'Your Company name must be at least {ruleValue} characters'
}
]
},
companyCode: {
identifier: 'companyCode',
rules: [
{
type : 'minLength[4]',
prompt : 'Your Company code must be at least {ruleValue} characters'
}
]
},
companyAddress: {
identifier: 'companyAddress',
rules: [
{
type : 'minLength[10]',
prompt : 'Your Company address must be at least {ruleValue} characters'
}
]
}
}
$("#companyForm").form(formValidationRules2);
$("#companyForm").form('validate form');
if($("#companyForm").form('is valid')){
joooo();
}
}

Adding multiple divs or renders in ReactJs

I'm having trouble in wanting to create multiples of the same html that I render in a certain class. For example, I have a div that might look like this
[Header goes here, is a input field] [Dropdown]
[TextArea]
[Submit]
[Add another field]
On add another field, I would like to clone this view and be able to add as many again.
Here's what I have so far:
var UpdateForm = React.createClass({
handleSubmit : function(e) {
e.preventDefault();
var title = React.findDOMNode(this.refs.title).value.trim();
var date = React.findDOMNode(this.refs.date).value.trim();
var body = React.findDOMNode(this.refs.body).value.trim();
if(!title||!date || !body ) {
return;
}
this.props.onSubmit({title:title, date : date, body : body});
React.findDOMNode(this.refs.title).value = '';
React.findDOMNode(this.refs.date).value = '';
React.findDOMNode(this.refs.body).value = '';
//React.findDOMNode(this.refs.sub).value = '';
},
render: function() {
return(
<div id = "This is what I want to duplicate on each button click">
<form className="form-horizontal" onSubmit={this.handleSubmit}>
<div class="form-control">
<label className="col-sm-0 control-label ">Title:</label>
<div class="col-sm-5">
<input className = "form-control" type = "text" placeholder="Title...." ref = "title"/>
</div>
</div>
<div class="form-control">
<label className="col-sm-0 control-label ">Date:</label>
<div class="col-sm-5">
<input className = "form-control" type = "text" placeholder="Date...." ref = "date"/>
</div>
</div>
<div className="form">
<label htmlFor="body" className="col-sm-0 control-label">Report Body:</label>
<div className="column">
<textarea className = "ckeditor" id = "ckedit" ref = "body"></textarea>
</div>
</div>
<div class="form-group">
<label className="col-sm-0 control-label"></label>
<div class="col-sm-5">
<input type = "submit" value="Save Changes" className = "btn btn-primary" />
</div>
</div>
</form>
<div className="btn-group">
<button type="button" className="btn btn-danger">Assign to</button>
<button type="button" className="btn btn-danger dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<span className="caret"></span>
<span className="sr-only">Toggle Dropdown</span>
</button>
<ul className="dropdown-menu">
{
this.props.forms.map(function(form){
return (
<li>{form}</li>
)})}
</ul>
<button className="btn btn-success btn-block" onClick={this.addInputField}>Add Subform</button>
</div>
</div>
);
}
});
What I think I need to add:
addDiv: function(e) {
e.preventDefault();
//have a array of divs?
//push a the same div into it?
//then set state of that array?
}
I know in jquery I could just write a function that appends this markup whenever I hit a button, but i don't know how to think about it here at all.
I think what you want is to have a button which add another header-date-body-Component to the form, which should then also be submitted, right?
If so then you need to think more in components. Have one Component which handles the form. Have one around that which handles adding other forms.
<ReportDialog>
<ReportForm>
<ReportForm>
<button onClick={this.addReport}>Add another</button>
</ReportDialog>
To accomplish the multiple ReportForms you need to think about the data in your component, which are reports (I assume). So you need a state in ReportDialog which keeps track of your reports. so at the start of the app you have one report:
getInitialState: function () {
return {
reports: [{ title: '', body: '', date: new Date() }]
};
}
So in addReport you then need to change the state and add another report. To have these reports rendered you already used map, but this time you need to loop over the reports in your component and return a ReportForm for each report.

Categories