Quasar change suffix of email depending on option group - vue.js

I'm trying to make a suffix change in an email input when I change an option.
Here is different parts of my code:
<q-field
icon="work"
label="Institution"
:label-width="3"
>
<q-option-group
type="radio"
v-model="institution"
#change="changeInstitution"
:options="[
{label: 'Institution 1', value: 'I1', suffix: '#institution1.fr'},
{label: 'Institution 2', value: 'I2', suffix: '#institution2.fr'}
]"
/>
</q-field>
<q-field
icon="email"
label="Adresse courriel"
:label-width="3"
>
<q-input v-model="email" type="email" suffix="" />
</q-field>
I also have these lines:
<script>
export default {
methods: {
changeInstitution () {
console.log('Change institution')
}
},
data () {
return {
institution: '',
email: ''
}
}
}
</script>
The problem is that when I change the "Institution" choice, I don't have the expected log ("Change institution"). Instead, I have this:
[Vue warn]: Missing required prop: "value"
found in
---> <QInput>
<QField>
<QTabPane>
<QTabs>
<QModal>
<QDialog>
<Testlogin> at src/components/views/testlogin.vue
<QToolbar>
<QLayoutHeader>
<QLayout>
<LayoutDefault> at src/layouts/default.vue
<Root>
Can anyone give me a hint? I looked at the documentation (http://quasar-framework.org/components/option-group.html#Installation), up to no avail...
Thanks in advance.

You're using v-model. This is equivalent to
:value="model" #input="(newVal) => model = newVal"
So, as a result, #change does not gets called, since #input is emitted first, changes the model, then Quasar components get to compare the emitting value to the model... but since v-model's #input changed model, now the emitting value is same, so Quasar components skip the #change event.
Either use:
v-model along with #input
The "lazy" equivalent of v-model (:value="model" #change="(newVal) => { model = newVal; callSomething...() }")

Related

v-mask not working correctly with veutifyjs input

I'm using veutifyjs text-input to show phone number. I have a button to assign phone number to v-model. But after assign it not change there text input. if I enter some think on text-input v-mask work. But if I assign value to v-model it not working. Also i'm tying next trick but its not have any difference.
<v-text-field
v-model="form.phone_number"
v-mask="'(###) ###-####'"
dense
outlined
autocomplete="new-password"
hide-details="auto"
label="Phone Number"
placeholder="Phone Number"
></v-text-field>
<v-btn color="warning" #click="dataupdate()">Add</v-btn>
dataupdate() {
this.$nextTick(() => {
this.form.phone_number = '4032223344'
})
},
You have to set the v-mask dynamically after you set the value of form.phone_number, so we could create a phoneNumberMask variable:
data() {
return {
phoneNumberMask: '',
};
}
Set it as the v-mask value:
<v-text-field
v-model="form.phone_number"
v-mask="phoneNumberMask"
...
then at dataupdate():
dataupdate() {
this.form.phone_number = '4032223344'
this.$nextTick(() => {
this.phoneNumberMask = '(###) ###-####'
})
/*
* you'd just have to deal with calling this funcition
* when phoneNumberMask has already a value
* and that depends on your business rules
*/
},
I had the same issue after migration to Vuetify 2 and fixed it so: Just replaced v-mask library with vue-input-facade as mentioned here.
It works exactly the same as the old mask from the first Vuetify but you should use the parameter "v-facade" instead.

Use v-model in a v-for loop

I have a component with the following data -
data() {
return {
name: '',
age: '',
}
}
I then define a method like the following -
formData() {
const data = [
{label: 'Name', model: this.name},
{label: 'Age', model: this.age},
]
return data
}
In my template, I am writing a v-for loop accessing the formData() to render the HTML. (I'm doing it this way because there will be ~50 form fields like name and age and the HTML is the same for every form field).
<md-table>
<md-table-row v-for="d in formData()" :key="d.label">
<md-table-cell>{{d.label}}</md-table-cell>
<md-table-cell>
<md-field>
<md-input v-model="d.model"></md-input>
</md-field>
</md-table-cell>
</md-table-row>
</md-table>
This template renders fine. However, the models are not bound, because the values of name and age does not change in the data if the user enters in the input fields.
I am pretty sure this is because when declaring formData(), I am actually passing the values of the data in the model. Is there a way I can actually pass the model, so that the template v-models bind?
Use props:
In the child component define:
props: ['name', 'age']
and remove the data element.

Prop in template not showing with VueJS

I've been trying to pass a prop in a component's template.
I think I'm missing some points here, and I didn't start to include my components in single files yet.
app.js
Vue.component('chat-response', {
props: ['response', 'senderClass'],
template: '<div>From {{ senderClass }} : {{ response.text }}</div>'
})
var app = new Vue({
el: '#app_chat',
data: {
responseList: [
{ id: 0, text: 'Response 1', type: 'Owner' },
{ id: 1, text: 'Response 2', type: 'Other' },
{ id: 2, text: 'Response 3', type: 'None' }
]
}
})
page.html
...
<chat-response v-for="response in responseList"
v-bind:key="response.id"
v-bind:response="response"
v-bind:senderClass="response.type"></chat-response>
...
Output :
From : Response 1
From : Response 2
From : Response 3
As we see, senderClass won't show up. I've tried different methods and only got errors that I could understand after reading around.
I don't wish to use response.type instead of senderClass because in the meantime, I'm setting senderClass after mounted with a real css class.
Maybe it's my approach that's completely wrong, could you give me some hints ?
I think the name of your property is wrong. Just change in page.html v-bind:senderClass="response.type" to v-bind:sender-class="response.type"
http://jsfiddle.net/eywraw8t/310360/
HTML attribute names are case-insensitive. Any uppercase character will be interpreted as lowercase. So camelCased prop names need to use their kebab-cased equivalents.
Apart from what Jns said You could get rid of v-bind altogether and just use :varibaleName for bindings.
Link to fiddle
https://jsfiddle.net/50wL7mdz/654614/#&togetherjs=J9vgbNaR7m

Vue.JS value tied on input having the focus

Is there a way to change a value in the model when an input gets/loses focus?
The use case here is a search input that shows results as you type, these should only show when the focus is on the search box.
Here's what I have so far:
<input type="search" v-model="query">
<div class="results-as-you-type" v-if="magic_flag"> ... </div>
And then,
new Vue({
el: '#search_wrapper',
data: {
query: '',
magic_flag: false
}
});
The idea here is that magic_flag should turn to true when the search box has focus. I could do this manually (using jQuery, for example), but I want a pure Vue.JS solution.
Apparently, this is as simple as doing a bit of code on event handlers.
<input
type="search"
v-model="query"
#focus="magic_flag = true"
#blur="magic_flag = false"
/>
<div class="results-as-you-type" v-if="magic_flag"> ... </div>
Another way to handle something like this in a more complex scenario might be to allow the form to track which field is currently active, and then use a watcher.
I will show a quick sample:
<input
v-model="user.foo"
type="text"
name="foo"
#focus="currentlyActiveField = 'foo'"
>
<input
ref="bar"
v-model="user.bar"
type="text"
name="bar"
#focus="currentlyActiveField = 'bar'"
>
...
data() {
return {
currentlyActiveField: '',
user: {
foo: '',
bar: '',
},
};
},
watch: {
user: {
deep: true,
handler(user) {
if ((this.currentlyActiveField === 'foo') && (user.foo.length === 4)) {
// the field is focused and some condition is met
this.$refs.bar.focus();
}
},
},
},
In my sample here, if the currently-active field is foo and the value is 4 characters long, then the next field bar will automatically be focused. This type of logic is useful when dealing with forms that have things like credit card number, credit card expiry, and credit card security code inputs. The UX can be improved in this way.
I hope this could stimulate your creativity. Watchers are handy because they allow you to listen for changes to your data model and act according to your custom needs at the time the watcher is triggered.
In my example, you can see that each input is named, and the component knows which input is currently focused because it is tracking the currentlyActiveField.
The watcher I have shown is a bit more complex in that it is a "deep" watcher, which means it is capable of watching Objects and Arrays. Without deep: true, the watcher would only be triggered if user was reassigned, but we don't want that. We are watching the keys foo and bar on user.
Behind the scenes, deep: true is adding observers to all keys on this.user. Without deep enabled, Vue reasonably does not incur the cost of maintaining every key reactively.
A simple watcher would be like this:
watch: {
user() {
console.log('this.user changed');
},
},
Note: If you discover that where I have handler(user) {, you could have handler(oldValue, newValue) { but you notice that both show the same value, it's because both are a reference to the same user object. Read more here: https://github.com/vuejs/vue/issues/2164
Edit: to avoid deep watching, it's been a while, but I think you can actually watch a key like this:
watch: {
'user.foo'() {
console.log('user foo changed');
},
},
But if that doesn't work, you can also definitely make a computed prop and then watch that:
computed: {
userFoo() {
return this.user.foo;
},
},
watch: {
userFoo() {
console.log('user foo changed');
},
},
I added those extra two examples so we could quickly note that deep watching will consume more resources because it triggers more often. I personally avoid deep watching in favour of more precise watching, whenever reasonable.
However, in this example with the user object, if all keys correspond to inputs, then it is reasonable to deep watch. That is to say it might be.
You can use a flat by determinate a special CSS class, for example this a simple snippet:
var vm = new Vue({
el: '#app',
data: {
content: 'click to change content',
flat_input_active: false
},
methods: {
onFocus: function(event) {
event.target.select();
this.flat_input_active = true;
},
onBlur: function(event) {
this.flat_input_active = false;
}
},
computed: {
clazz: function() {
var clzz = 'control-form';
if (this.flat_input_active == false) {
clzz += ' only-text';
}
return clzz;
}
}
});
#app {
background: #EEE;
}
input.only-text { /* special css class */
border: none;
background: none;
}
<!-- libraries -->
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" rel="stylesheet"/>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<!-- html template -->
<div id='app'>
<h1>
<input v-model='content' :class='clazz'
#focus="onFocus($event)"
#blur="onBlur"/>
</h1>
<div>
Good luck
You might also want to activate the search when the user mouses over the input - #mouseover=...
Another approach to this kind of functionality is that the filter input is always active, even when the mouse is in the result list. Typing any letters modifies the filter input without changing focus. Many implementations actually show the filter input box only after a letter or number is typed.
Look into #event.capture.

Observe Change in Vue js model when input updated by javascript function

I am using Date Range Picker to select to dates now when I select the dates, I update the inputs with dates value respectively.
The inputs I have binded with v-model and created a function in watch attribute of component to observe the change in model.
But when the inputs are updated with the javascript function no change can be observed in the model but the value of my input fields are updated.
// My Input Fields
<input type="text" name="updateStartDate" v-model="updateDateRange.start">
<input type="text" name="updateEndDate" v-model="updateDateRange.end">
//My javascript Function
$('input[rel=dateRangePickerX]').daterangepicker({
'autoApply': true,
'drops': 'up',
'startDate': moment().add(90, 'days').calendar(),
'endDate': moment().add(97, 'days').calendar(),
locale: { cancelLabel: 'Clear' }
},
function (start, end, label) {
$('input[name="updateStartDate"]').val(start.format('MM/DD/YYYY'));
$('input[name="updateEndDate"]').val(end.format('MM/DD/YYYY'));
});
// My watch attribute in Component
watch : {
'updateDateRange.end' : function (val) {
console.log('In Watch Function');
console.log(this.dateRanges);
if(val != '' && this.updateDateRange.start != '' && this.updateDateRangeIndex != ''){
console.log(val);
console.log(this.updateDateRange.start);
console.log(this.updateDateRangeIndex);
this.dateRanges[this.updateDateRangeIndex] = this.updateDateRange;
this.updateDateRangeIndex = '';
this.updateDateRange.start = '';
this.updateDateRange.end = '';
console.log(this.dateRanges);
}
}
}
I don't like to mix jQuery and Vue because jQuery messes up the DOM. Even more, I find it completely unnecessary.
Simple only with native Vue you can do it like this:
<input type="text" name="updateStartDate" v-model="startDate" #input="onInput()">
<input type="text" name="updateStartDate" v-model="endDate" #input="onInput()">
methods: {
onInput(e): function () {
// this will be called on change of value
}
}
Further to set the value and update the DOM simply update startDate and/or endDate variables and DOM will update accordingly.
You need to work with your model and not fiddle with the bound DOM element. You have bound the elements to viewmodel items:
<input type="text" name="updateStartDate" v-model="updateDateRange.start">
<input type="text" name="updateEndDate" v-model="updateDateRange.end">
then you use jQuery to set the field values
$('input[name="updateStartDate"]').val(start.format('MM/DD/YYYY'));
$('input[name="updateEndDate"]').val(end.format('MM/DD/YYYY'));
but you should be setting the bound values instead:
updateDateRange.start = start.format('MM/DD/YYYY');
updateDateRange.end = end.format('MM/DD/YYYY');