Vue.js: Calling function on change - javascript

I'm building a component in Vue.js. I have an input on the page where the user can request a certain credit amount. Currently, I'm trying to make a function that will log the input amount to the console, as I type it in. (Eventually, I'm going to show/hide the requested documents based on the user input. I don't want them to have to click a submit button.)
The code below logs it when I tab/click out of the input field. Here's my component.vue:
<template>
<div class="col s12 m4">
<div class="card large">
<div class="card-content">
<span class="card-title">Credit Limit Request</span>
<form action="">
<div class="input-field">
<input v-model="creditLimit" v-on:change="logCreditLimit" id="credit-limit-input" type="text">
<label for="credit-limit-input">Credit Limit Amount</label>
</div>
</form>
<p>1. If requesting $50,000 or more, please attach Current Balance Sheet (less than 1 yr old).</p>
<p>2. If requesting $250,000 or more, also attach Current Income Statement and Latest Income Tax Return.</p>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'licenserow',
data: () => ({
creditLimit: ""
}),
methods: {
logCreditLimit: function (){
console.log(this.creditLimit)
}
}
}
</script>
If I change methods to computed, it works - but I get an error saying Invalid handler for event: change every time it logs the value.

Use the input event.
<input v-model="creditLimit" v-on:input="logCreditLimit" id="credit-limit-input" type="text">
change only fires when the element loses focus for input elements. input fires each time the text changes.

Binding #input event alongside with v-model is unnecessary. Just bind v-model and thats all. Model is automatically updated on input event.
new Vue({
el: '#app',
data: {
message: ''
}
})
<script src="https://unpkg.com/vue#2.4.4/dist/vue.min.js"></script>
<div id="app">
<input type="text" v-model="message"><br>
Output: <span>{{ message }}</span>
</div>
And if you need log it on change to console, create particular watcher:
new Vue({
el: '#app',
data: {
message: ''
},
watch: {
message: function (value) {
console.log(value) // log it BEFORE model is changed
}
}
})
<script src="https://unpkg.com/vue#2.4.4/dist/vue.min.js"></script>
<div id="app">
<input type="text" v-model="message"><br>
Output: <span>{{ message }}</span>
</div>

<input v-model="yourVariableName" #input="yourFunctinNameToBeCall" id="test" type="text">
You can use #change to it trigger the function when is done
Ex- Select a value from drop down
But here you need to call the function when pressing keys (enter values). So use #click in such cases

Related

How to set value of argument event target on vue?

I have an input value using debounce plugin, that passing to the event. The input dom is based on an array inside looping.
At some conditions, I need to set the value that the input box to “0” from the event action after being compared with another data. How to do that?
My template code
<div class="form-group row">
<label class="col-form-label col-lg-2">QTY</label>
<div class="col-lg-10">
<div class="input-group" style="width: 300px !important">
<input
type="text"
class="form-control"
#input="CalculateItem"
v-model="item.qty"
/>
<span class="input-group-append">
<span class="input-group-text">Carton</span>
</span>
</div>
</div>
Vue method :
CalculateItem: _.debounce(function (e) {
console.log(e.target);
var totalItem = _.sumBy(this.itemList, (item) => Number(item.qty));
if(this.lineTotal<totalItem){
this.dialogOverqty = true;
e.target.value=0;
}
else{
this.lineamount = this.lineTotal - totalItem;
}
}, 500),
Have tried :
e.target.value=0; //not working
Do not change the value of the input element in the DOM. Change the data bound as v-model
To get access to correct item in the event handler, just pass the item into the handler and use $event to pass the original event data as well (if you actually need it)
<input
type="text"
class="form-control"
#input="CalculateItem($event, item)"
v-model="item.qty"
/>
Now you can change item.qty inside CalculateItem and Vue will update the content of the <input>
Also note that when creating debounced function like that, there is only one debounced function for all instances of given component - see the docs (yes, the docs are for Vue 3 but same applies to Vue 2)

How can I get a input required with vuejs

I have my chat and I dont want people to send empty message so I would like that my input become required. Thanks for your help.
I tried to put "required='required'" in the input line, I also tried veeValidate but it broke my chat when I use it, I also tried to put "Required = true" in Props and data but without a good result
This is ChatForm.vue
<template>
<div class="input-group" >
<input id="btn-input" type="text" name="message" class="form-control input-sm" placeholder="Ecrire..." v-model="newMessage" #keyup.enter="sendMessage">
<span class="input-group-btn">
<button class="btn btn-primary btn-sm" id="btn-chat" #click="sendMessage">
&#10003
</button>
</span>
</div>
</template>
<script>
export default {
props: ['user'],
data() {
return {
newMessage: '',
}
},
methods: {
sendMessage() {
this.$emit('messagesent', {
user: this.user,
message: this.newMessage
});
setTimeout(function() {
const messages = document.getElementById('mess_cont');
messages.scrollTop = messages.scrollHeight;
}, 200);
this.newMessage = '';
}
}
}
</script>
And this is my form in the app.blade.php
<div id="app" class="container-chat">
<div class="row">
<div class="col-md-12 col-md-offset-2">
<div class="col-md-12 col-md-offset-2">
<div class="panel-body panel-content" id="mess_cont">
<chat-messages id="mess" :messages="messages" :currentuserid="{{Auth::user()->id}}"></chat-messages>
</div>
<div class="panel-footer">
<chat-form
v-on:messagesent="addMessage"
:user="{{ Auth::user() }}"
></chat-form>
</div>
</div>
</div>
</div>
</div>
Try to change your ChatForm.vue like this:
<template>
<form #submit.prevent="sendMessage">
<div class="input-group" >
<input id="btn-input" type="text" name="message" class="form-control input-sm" placeholder="Ecrire..." v-model="newMessage" required>
<span class="input-group-btn">
<button class="btn btn-primary btn-sm" type="submit" id="btn-chat">
&#10003
</button>
</span>
</div>
</template>
You are not treating the input in the correct way, the input which is required needs to be inside a form and the required keyword will prevent the form submission if the input field is empty.
There are a few things I would do differently.
1/ Wrap your chat form in a tag, and execute the sendMessage() method on submit. This will give your users a nicer experience, as they can just to submit the message.
2/ Convert the button into a submit button so it triggers the form.submit event.
3/ You can easily disable the button by checking whether newMessage has contents. I don't think you need vee validate or anything else to achieve this; for something as simple as a chat form, your user doesn't need much more feedback than seeing a disabled button to realise (s)he needs to write something first.
4/ in the addMessage method you can just check the contents of newMessage and not do anything when it's empty. This is perfectly fine because you already hinted the user by disabling the button too.
I think this is a subtle way where you guide your user, but don't overdo it.
Please add name attributes to all of your form elements. Some of the element in my form had name attribute and some didn't. Element which had name attributes worked correctly but the one's which didn't had name failed.

Why is my boolean always true when passing it as value to a radio component?

so I am learning vue and have spent some time going through the documents and haven't seen the answer that solves my question. A lot of this is due to the nomenclature between using the CLI(which I am) and not.
I am trying to make it so that when one radio button is clicked it shows a div and when the other one is clicked it shows the other. Here is what I have.
Template:
<div id="daySelection">
<div class="o-m-day">
<div id="oneDay">
<p>One day?</p><input v-model="selected" type="radio" name="oneDay" id="" class="r-button" value="true">
</div>
<div id="multipleDays">
<p>Multiple days?</p> <input v-model="selected" type="radio" name="multDays" id="" class="r-button" value="false">
</div>
</div>
<!-- the div where the conditional render will be rendered -->
<div>
<!-- multiple days -->
<div v-show="selected" id="ta-multDays">
<textarea rows="10" cols="80" name="multDays" type="text" />
</div>
<!-- one day -->
<div v-show="!selected" id="i-oneDay">
<input type="text" name="r-oneDay">
</div>
</div>
</div>
Here is the script:
export default {
name: 'CreateTournamentForm',
data: function(e) {
return {
selected: Boolean,
}
},
}
above I was getting an error in the console that was saying that data needs to be a function that returns a new instance. I see many people and examples using vue instances differently where it is:
const app = new Vue({
el: '#app',
data: {
selected: true,
}
});
However whenever trying this Vue sends me a warning saying that it needs to be a function.
[Vue warn]: The "data" option should be a function that returns a per-instance value in component definitions.
I am also aware that v-show toggles the display so I have tried both setting the display of the divs to:
display: none;
as well as not.
The problem is that the value of selected is a string, whereas you expect it to be a boolean.
The following watcher:
watch: {
selected(newValue) {
console.log("selected changed to", newValue, "which is a", typeof newValue);
}
}
Will tell you this:
selected changed to true which is a string
selected changed to false which is a string
The reason is that you give the fields value a string instead of a boolean. To fix this, instead of writing value="true", write :value="true".
You can play with a live example here.
There are two problems as far as I can see here:
In a component, the data key must be a function and the value for the selected key in the object returned by the data function must be an actual boolean value true or false (which will be initial value)
export default {
name: 'CreateTournamentForm',
data: function(e) {
return {
selected: true,
}
},
}
By default, v-model values are treated as strings so your true and false values are actually the strings "true" and "false" which are both truthy. Changing your template code to the below (or alternatively using a number or string value instead) should fix it
<div v-show="selected === 'true'" id="ta-multDays">
<textarea rows="10" cols="80" name="multDays" type="text" />
</div>
I solved it by changing it from a 'v-show' to 'v-if'
<div>
<p>One day?</p>
<input
v-model="selected"
type="radio"
name="oneDay"
id="oneDay"
class="r-button"
value="onlyOneDay" />
</div>
<div id="multipleDays">
<p>Multiple days?</p>
<input
v-model="selected"
type="radio"
name="multDays"
id="multDays"
class="r-button"
value="multipleDays" />
</div>
then the div to be shown as follows:
<div v-if="selected === 'multipleDays'" id="ta-multDays">
<textarea rows="10" cols="80" name="" type="text" />
</div>
<div v-if="selected === 'onlyOneDay'" id="i-oneDay">
<input type="text" name="">
</div>

Disable a sibling button if reactive form field is not valid Angular 5

I have a component with a reactive form in it like so...
form component.html
<form [formGroup]="form" class="do-form">
<div formGroupName="dot">
<div class="do-form__container">
<div class="do-form__group">
<label for="day">Day</label>
<input id="day" type="number" placeholder="XX" class="do-form__group__control" formControlName="day" />
</div>
<div class="do-form__group">
<label for="month">Month</label>
<input id="month" type="number" placeholder="XX" class="do-form__group__control" formControlName="month" />
</div>
<div class="do-form__group">
<label for="year">Year</label>
<input id="year" type="number" placeholder="XXXX" class="do-form__group__control" formControlName="year" />
</div>
</div>
<div class="do-form__errors" *ngIf="isValid()">
<p>Please enter a valid date</p>
</div>
</div>
</form>
and in my form.component.ts
form = this.fb.group({
dot: this.fb.group({
day: ['',
[
Validators.required,
Validators.min(1),
Validators.max(31)
]],
month: ['',
[
Validators.required,
Validators.min(1),
Validators.max(12)
]],
year: ['2018',
[
Validators.required,
Validators.min(1918),
Validators.max(2018)
]]
})
});
isValid() {
return (
!this.form.valid &&
this.form.get('dot.day').touched &&
this.form.get('dot.month').touched &&
this.form.get('dot.year').touched
);
}
Now I have a separate page (app.component.html) like so
<app-form #formTool></app-form>
<button class="full-width-btn" (click)="next(); form.sendResult();">SEND</button>
app.component.ts
import { formComponent } from '../form-component/form-component.ts'
export ...
#ViewChild(formComponent) form;
Now basically I want to disable the send button until the form in the app form component is valid.
I'm not entirely sure how to do this. I thought about storing a valid event on a shared server but I'm not sure how I can store a valid event in a service. I saw that with non-reactive forms you can just have a button that uses the same ngModel but once again not sure if that would work in this case.
Any help would be appreciated!
EDIT
I have tried [disabled]="form.invalid" and [disabled]="!isValid()" but I am still able to click the button
I have also tried using [disabled]=!form.isValid() and [disabled]=!form.form.isValid()
Thanks
You are really close. Here is the only thing you need to add:
<app-form #formTool></app-form>
<button class="full-width-btn" [disabled]="!isValid()" (click)="next(); form.sendResult();">SEND</button>
In form component you could define an event #Output formValid = new EventEmitter().
Then you can listen to changes in input fields (on keypress or so) and on any change check the validity and if the form is valid, emit an event: formValid.emit().
In the app component define formIsValid = false, and on the app-form element you can listen to the formValid event:
< app-form (formValid)="formIsValid = true">
(or some function in app.component.ts instead of the inline code).
And finally on button
< button [disabled]="!formIsValid">

Field validation onBlur with Aurelia

I'm using Aurelia and Typescript to build a web page. I have a simple login form and I'd like to validate the user email and password.
I am using Aurelia validation and by default it validates the content of my input each time it changes, which can be annoying. (ex: getting an error message saying that the email is not valid when you're not even done typing it). So I'd like to do the validation onBlur instead (when the focus on the input is lost) and when the user clicks on the Login button.
Here's my code:
login.html
<template>
<section>
<div class="container col-lg-12">
<div class="col-md-4 col-md-offset-4 centered">
<h2 t="signin_sign_in"></h2>
<form role="form" submit.delegate="login()" validate.bind="validation">
<br if.bind="errors" />
<div if.bind="errors" repeat.for="error of errors" class="alert alert-danger">
<h4 repeat.for="message of error">${message}</h4>
</div>
<div class="form-group">
<label class="control-label" t="signin_email_address"></label>
<input type="text" class="form-control" value.bind="email">
</div>
<div class="form-group">
<label class="control-label" t="signin_password"></label>
<input type="password" class="form-control" value.bind="password">
</div>
<button type="submit" class="btn btn-primary" t="signin_sign_in"></button>
</form>
</div>
</div>
</section>
</template>
login.ts
#autoinject()
export class Login {
email: string;
password: string;
router: Router;
application: ApplicationState;
accountService: AccountService;
errors;
validation;
i18n: I18N;
constructor(router: Router, application: ApplicationState, accountService: AccountService, validation: Validation, i18n: I18N) {
this.router = router;
this.application = application;
this.accountService = accountService;
this.i18n = i18n;
this.errors = [];
this.validation = validation.on(this)
.ensure('email')
.isNotEmpty()
.isEmail()
.ensure('password')
.isNotEmpty()
.hasLengthBetween(8, 100);
}
navigateToHome(): void {
this.router.navigate("/welcome");
}
login(): void {
var __this = this;
this.validation.validate()
.then(() => this.accountService.signin(this.email, this.password, this.rememberMe)
.then(result => {
// Do stuff
})
.catch(error => {
// Handle error
}
}));
}
}
My first thought was to add
& updateTrigger:'blur':'paste'
to my binding in the HTML, but it doesn't work. The binding is updated correctly when focus is lost but the validation stops working. There's no error in the Chrome debug console either.
Any idea on how to do this? Is is possible at all?
There's different binding behaviours you can use to tell when the validation should trigger. You can read more about them in the Aurelia docs on validation.
From the docs;
The validate binding behavior obeys the associated controller's
validateTrigger (blur, change, changeOrBlur, manual). If you'd like to
use a different validateTrigger in a particular binding use one of the
following binding behaviors in place of & validate:
& validateOnBlur: the DOM blur event triggers validation.
& validateOnChange: data entry that changes the model triggers validation.
& validateOnChangeOrBlur: DOM blur or data entry triggers validation.
& validateManually: the binding is not validated
automatically when the associated element is blurred or changed by the
user.
I recently encountered this exact use case and I solved it using Aurelia's built in debounce feature.
<input type="text" class="form-control" id="email" placeholder="Email"
value.bind="email & validateOnChangeOrBlur & debounce:600 ">
600ms is an arbitrary value, but you can always play around with it as needed.

Categories

Resources