I'm building a basic 'required' form validation function. Here's the function:
JS:
export default {
methods: {
required(string) {
if (!string) {
return 'This field is required!'
}
}
}
}
HTML:
<input id="username"
v-model="credentials.username"
type="text"
name="username"
/>
<span>{{ required(credentials.username) }}</span>
The above works great. If I start typing in the input, the returned value goes null. If I empty the input, the returned value comes back as expected, "This field is required".
My question is, how can I return the value as null/blank to start? Expected flow is:
Returned value is null/blank to start
User starts typing, nothing changes because string.length != 0
User deletes all characters, causing string.length == 0, causing the returned value to be 'This field is required!'
One solution is to use an input-event handler (called for every new value in the input) that sets a flag to indicate the field is "dirty". Then conditionally render the validation result (the <span>) based on the flag:
Declare a data property named "dirty":
export default {
data() {
return {
dirty: false,
}
}
}
In the template, add an input-event handler on the <input> that sets the dirty flag:
<input #input="dirty = true">
Also, conditionally render the <span> field based on dirty:
<span v-if="dirty">{{ required(credentials.username) }}</span>
demo
Related
I have the following input:
<input :value="inputAmount" #input="handleAmountInput($event)" placeholder="Enter amount..." type="text">
I don't want 2-way binding with inputAmount because I want to clean the input of non-numeric characters in the handleAmountInput() function whenever the user inputs something:
handleAmountInput(e) {
const cleanInput = e.target.value.replace(/\D/g, '');
this.inputAmount = cleanInput;
console.log(cleanInput);
},
The issue is, the input itself doesn't reflect this cleaned up string set to inputAmount. If I show inputAmount in a separate element or console.log it like in the snippet above, it shows up just fine, but binding the value to the input with :value doesn't seem to work and shows the full inputted string, including non-numeric characters. What am I doing wrong here and how do I get the input to show the cleaned up string?
I'm not yet sure why exactly your code doesn't work as I would expect it to, but the way to fix it is to use both v-model and #input handler at the same time...
const app = Vue.createApp({
data() {
return {
inputAmount: ''
}
},
methods: {
handleAmountInput(e) {
this.inputAmount = e.target.value.replace(/\D/g, '');
console.log(this.inputAmount);
},
},
})
app.mount('#app')
<script src="https://unpkg.com/vue#3.1.5/dist/vue.global.js"></script>
<div id='app'>
<input v-model="inputAmount" #input="handleAmountInput($event)" placeholder="Enter amount..." type="text">
<pre>{{ inputAmount }}</pre>
</div>
Update
Ok, I now understand the reason why your code does not work. What happens:
Value of inputAmount is for example '123' (reflected in <input>)
User types a
Your #input handler is called. It receives the value '123a', do it's job creating cleaned value '123' and assigns it into inputAmount
From Vue POV the value of inputAmount did not changed at all so no re-render is required and <input> still shows '123a' even tho inputAmount has a value of '123'
So another way of fixing your code is just to assign some value <input> can never produce into inputAmount 1st just to trigger the update (demo below)
const app = Vue.createApp({
data() {
return {
inputAmount: ''
}
},
methods: {
handleAmountInput(e) {
this.inputAmount = null
this.inputAmount = e.target.value.replace(/\D/g, '');
console.log(this.inputAmount);
},
},
})
app.mount('#app')
<script src="https://unpkg.com/vue#3.1.5/dist/vue.global.js"></script>
<div id='app'>
<input :value="inputAmount" #input="handleAmountInput($event)" placeholder="Enter amount..." type="text">
<pre>{{ inputAmount }}</pre>
</div>
Have you tried using #change event
<input :value="message" #change="getInput($event)" placeholder="edit me" />
Use computed getter setter instead, Link :
example :
computed: {
inputAmount: {
get(){
//perform your logic
return 'value'
},
set(newValue){
this.value= newValue;
}
}
}
use v-model="inputAmount"? please see: https://cn.vuejs.org/v2/guide/forms.html
then you can just edit like this.inputAmount= this.inputAmount.replace(/\D/g, '');
I am currently trying Nuxt.js with Vuex. And I Built a simple form, where I have an email field, a password field and a button.
All my state, mutations and actions are working as they should be. But When I created a computed property just to validate the password, I always have an error with an if statement to validate length of the password.
My Vuex state looks like this:
export default () => ({
// Register Init States
registerEmail: null,
registerPassword: null,
})
My mutation:
export default {
setRegisterEmail(state, registerEmail) {
state.registerEmail = registerEmail
},
setRegisterPassword(state, registerPassword) {
state.registerPassword = registerPassword
},
}
My template:
<vs-input
:value="registerPassword"
label="Password"
primary
type="password"
:progress="getProgress"
:visible-password="hasVisiblePassword"
icon-after
#input="setRegisterPassword"
#click-icon="hasVisiblePassword = !hasVisiblePassword"
>
<template #icon>
<i v-if="!hasVisiblePassword" class="bx bx-show-alt"></i>
<i v-else class="bx bx-hide"></i>
</template>
<template v-if="getProgress == 100" #message-success
>Secure password</template
>
</vs-input>
My Computed property:
getProgress() {
let progress = 0
// at least one number
if (/\d/.test(this.$store.state.auth.registerPassword)) {
progress += 30
}
// at least one capital letter
if (/(.*[A-Z].*)/.test(this.$store.state.auth.registerPassword)) {
progress += 20
}
// at least a lowercase
if (/(.*[a-z].*)/.test(this.$store.state.auth.registerPassword)) {
progress += 25
}
// more than 8 digits
if (this.$store.state.auth.registerPassword === null) {
progress += 0
} else if (this.$store.state.auth.registerPassword.length >= 8) {
progress += 25
}
return progress
},
So because the password init state is null, there should be no progress, but as I type the password, it should to the else if and verify the number of chars.
But when I type the password, the input and my state only keeps the last letter I typed.
Imagine that I typed "overflow", my state password would only have "w". And if I remove the password validation length, my state looks like this "overflow".
Am I doing something wrong? I hope I was clear 🥺 Because I am so confused right now. 😩
The problem is being caused when you call setRegisterPassword in the template. That input event is only returning the last input character. You can add a handler to update that value properly. One option is to use a local data property as a v-model binding and then setRegisterPassword to that value in the input handler.
<vs-input
v-model="localPassword"
label="Password"
primary
type="password"
:progress="getProgress"
:visible-password="hasVisiblePassword"
icon-after
#input="handleRegisterPassword"
#click-icon="hasVisiblePassword = !hasVisiblePassword"
>
and in your
data(){
return {
localPassword:''
}
},
methods: {
handleRegisterPassword() {
this.setRegisterPassword(this.localPassword);
}
}
Note: I sam not familiar with vs-input so your :value may work the same as v-model so you may be able to leave that as value.
I have an input field and add button in a component.
Button is disabled at first, when user enters valid format url, button becomes active, then button add this domain to db, refresh table(which is in another component). I want to achieve that, after button is clicked it makes input field empty (refresh like page is just opened)
My component template is
<form #addDomainForm="ngForm" (ngSubmit)="addClicked()">
<mat-card class="topia-box-shadow">
<mat-card-content fxLayout="row wrap" fxLayoutAlign="left" fxLayoutGap="16px">
<div>
<p>Enter domain name or copy and paste the full link to add its domain
to white list</p>
<mat-form-field>
<mat-label>Enter link here</mat-label>
<input type="url" matInput [(ngModel)]="domainName" #url="ngModel"
[class.is-invalid]="url.invalid && url.touched"
name="url" [pattern]="urlValidationRegexPattern" required>
</mat-form-field>
<mat-error *ngIf="url.invalid && (url.dirty || url.touched)">
Please enter the link in correct format
</mat-error>
</div>
</mat-card-content>
<button type="button" id="search" class="topia-btn topia-primary-btn action-buttons"
[disabled]="!addDomainForm.valid" (click)="addClicked()">Add
Domain
</button>
</mat-card>
</form>
My ts file for that component:
export class DomainWhiteListingAddComponent implements OnInit {
#Output()
domainAdded = new EventEmitter<true>();
domainName: string;
public urlValidationRegexPattern = /^((((https?)(:\/\/))?((www)\.)?)?|mailto:(\w+\.?\w+)\#)([a-z0-9]+\.[a-z0-9]+)+((\/([\w#]+|[\w#]+(([.\w#])*(-\w+|=\w+|\?\w+=\w+(&\w+=(\w)+)*)?)+)+))*$/;
constructor(private globalSettingService: GlobalSettingsService, private dialog: MatDialog) {
}
ngOnInit() {
}
addClicked() {
const newDomainModel = {
id: null, domain: this.domainName, disabled: false
} as Domain;
this.globalSettingService.addDomainToList(newDomainModel)
.subscribe((data: Domain) => {
this.domainAdded.emit(true);
}, (error: HttpErrorResponse) => {
this.showErrorDialog(error.error);
});
}
}
This code is working but after clicking button and adding the element, input box value still stays there.
if i add a single line code to .ts file addClicked() method
this.domainName = null;
this time, input field becomes empty as I wanted but it appears as invalid so it shows error. I just want it make as default like the page is opened at beginning. Like
But it shows as:
How can I do that ? (if it is needed i can add parent component codes and other component code also)
Or if it is needed; how can i reset just that component?
How about
#ViewChild(addDomainForm) formDirective: FormGroupDirective;
addClicked() {
this.formDirective.resetForm();
}
It'll reset your form as you are checking if the form is dirty
You are making the ngModel domainName as null so it throws error.
Change it like this.domainName = ''
Along with setting the ngModel to it's original value, empty or null. You should also set your form asPristine.
If you are using Template-Driven forms and you have something like this in your component: #ViewChild('myForm') myform: any;
You can use the markAsPristine() function on the form property of your form. So it would be this.myform.form.markAsPristine().
In your HTML you use url.invalid && url.touchedto add the invalid red class, perhaps try this instead:
[class.is-invalid]="url.invalid && url.dirty"
This way it's not flagged as invalid until the value as actually changed.
I am now calling controller methods when this inputfield is changing :
<input type="text" ng-model="query" ng-model-options='{ debounce: 500 }' ng-change="searchOnTags(query)"/>
How can I call a different method when the input field is empty again?
You can just use if statement inside your searchOnTags function like this:
$scope.searchOnTags = function(query) {
console.log(query);
if (!query) {
console.log('input is empty');
}
};
Inside your searchOnTags function check whether the form is $pristine, which means the input field is back to its initial state.
When you edit a field it become dirty(ng-dirty), when you clear the input you can set the form to pristine state(ng-pristine).
I am currently building a fairly complex form with a lot of conditional logic. by that I mean, when users check one option, 1 or more others maybe revealed or hidden - you've probably seen it many times before..
Using Ractive's mustache templates and many {{#if }} statements I have created the form, but the logic for validation and submission needs improvement. I need to enable the submit button only when all 'visible' fields are valid, so I have concluded that each field needs an isInUse property as well as isValid, see below for an example:
data: {
foo: {
isValid: false,
isInUse: false,
value: ''
}
}
The reason for this is that a field could be made visible but then an option could hide it & it might still have a value that the user does not need to submit.
I have also determined that the only reliable way to change the isInUse property is to create a function in my data that can be accessed from my template, like so:
data: {
foo: {
isValid: false,
isInUse: false,
value: ''
},
isInUse: function (keypath, bool) {
ractive.set(keypath, bool);
}
}
which is used in my templates like so:
{{#if choice.email}}
{{ isInUse('validate.email.isInUse', true) }}
{{ isInUse('validate.phone.isInUse', false) }}
<label for="email">Email</label>
<input type="text" id="email" value="{{validate.email.value}}">
{{/if}}
This means I am able to change the value on the template-side.. which means I can check if each field is in use and valid.. now this is where I am questioning the implementation, is this a good idea?
I have created a simple version of the form on jsbin (which completely works with validation & submission), see here: http://jsbin.com/wasoxa/2/edit?html,js,output but my form is much more complex so I'd like to find a graceful way of handling all of this.
Calling isInUse from within the template is a very creative solution, but unfortunately very likely to break!
You should think of expressions in templates as being read-only - you can call a function within an expression, but only to get its value, never for side-effects such as setting another value (the one possible exception being to log output for debugging). The reason is that you're not in direct control of when the function is called - Ractive handles that on your behalf - so you can get unexpected results. In the example above, changing choice.email from true to false won't have the desired effect.
You probably want computed properties. These can be read inside the template just like regular properties, except that their value depends on other data (or other computed properties):
ractive = new Ractive({
el: 'body',
template: 'twice {{foo}} is {{doubleFoo}}',
data: { foo: 1 },
computed: {
doubleFoo: function () {
return 2 * this.get( 'foo' );
}
}
});
Whenever foo changes, doubleFoo knows (because we called this.get('foo') inside its definition) that it should recompute itself. You can use computed values just like you'd use any other value - e.g. ractive.observe('doubleFoo',doSomething).
This can be useful for validation:
var ractive = new Ractive({
el: 'main',
template: `
<h2>contact type</h2>
<label>
<input type="radio" name="{{contactType}}" value="email"> email
</label>
<label>
<input type="radio" name="{{contactType}}" value="telephone"> telephone
</label>
<h2>name</h2>
<input type="text" value="{{name}}">
<p>name is valid: {{nameIsValid}}</p>
{{#if contactType === "email"}}
<h2>email</h2>
<input type="text" value="{{email}}">
<p>email is valid: {{emailIsValid}}</p>
{{/if}}`,
computed: {
nameIsValid: function () {
return !!this.get( 'name' );
},
emailIsValid: function () {
var email = this.get( 'email' );
// never actually use this regex
return /^\w+#[a-zA-Z_]+?\.[a-zA-Z]{2,3}$/.test( email );
}
}
});
<script src="http://cdn.ractivejs.org/latest/ractive.js"></script>
<main></main>