I want to use in my app on vuejs 3 this library intl-tel-input.
There is a ready component vue3-tel-input.
Problem: directive v-model doesn't work - variable from data that passed as a model to component doesn't changed.
Every input event, which emitted from component, works three times - visible in the console.
Sanbox
Best regards.
v-model doesn't work in vue3-tel-input because that component hasn't migrated its v-model implementation to Vue 3. The only migration that component seems to have completed is the plugin installation.
In Vue 2, the model property was named "value" and the model-update event was "input". However in Vue 3, they've been renamed to "modelValue" and "update:modelValue", respectively. Notice how vue3-tel-input still uses "value" and "input".
A workaround is for the consumer component to manually bind value and listen to input events, effectively implementing Vue 2 v-model in the parent:
<template>
<vue-tel-input :value="phone" #input="onInput"></vue-tel-input>
<div>{{ phone }}</div>
</template>
<script>
import { VueTelInput } from 'vue3-tel-input'
import 'vue3-tel-input/dist/vue3-tel-input.css'
export default {
components: {
VueTelInput
},
data() {
return {
phone: '+79991234567',
}
},
methods: {
onInput(phone, phoneObject, input) {
if (phoneObject?.formatted) {
this.phone = phoneObject.formatted
}
}
}
}
</script>
demo
Related
while trying to write a component test by using vue test utils, testing interaction between child components and stuff, I am stuck due to usage of Vuelidate from child components. Below is an example simplified:
// parent component code
<template>
<div>
<childA />
</div>
</template>
//childA code
<template>
<input v-model="value" />
</template>
<script>
...
validations: {
value: {
required
}
}
...
</script>
// parent component test
...
const wrapper = mount(MyParentComponent, {
...,
components: {
childA,
},
validations: {
value: required
},
...
})
I have tried to find a solution out there that I could mount (note here that I WANT to mount also the child components, so shallow-mount is not what I look for) the child component, with it's respective Vuelidate validation rules, but I still haven't found any solution.
Instead, my test gives me errors like:
Cannot read property `value` of undefined
which makes sense, since the test cannot access the child component's $v instance.
Has anyone achieved it so far?
For answering your question and after i've did some test i believe you missed the data part inside your mount
mount: render child components
shallowMount: doesn't render child components
MyParentComponent need to have in the options the structure of you're child component so this is why he is returning the error
And i saw that you're passing the import of your component directly but don't forget that your test folder is outside of your src folder
import ChildA from "#/components/ChildA";
will not work instead i propose to use absolute path directly to import your child component or use a configuration to resolve them
const wrapper = mount(MyParentComponent, {
data() {
return {
value: null
}
},
components: {
ChildA: () => import('../../src/components/ChildA'),
},
validations: {
value: required
},
})
As simplified below, my app has a template with a custom component.
The data is passed from Template A to custom component as props (":list")
Template A:
<template>
...
<custom-component
v-for="list in listGroup"
:key="list.id_list"
:list="list"
/>
</template>
<script>
export default {
data() {
return {
listGroup: []
};
},
components: {
'custom-component':require("...").default
}
</script>
The custom component
<template>
...
</template>
<script>
export default {
props:["list];
...
}
</script>
Problem to solve:
A new item is added to the list sent as props.
I need the list (:list="list") to be dynamically updated so that the props in the custom component automatically reflect that update.
Thanks.
There are two ways to achieve that one way is to use a state management library(Vuex is recommended) the other is to use events.
Here is an example of using events:
create a file event-bus.js with the following content
import Vue from "vue";
export const EventBus = new Vue();
then in your component where you want to update list use this EventBus.$emit('eventName', data);
remember to import event-bus file
the listen to the event in the other component
EventBus.$on('eventName', function (details) {
//update list here
});
I am trying to make a visual representation of a component library. I am using dynamic <component>s to render each component. However, as I am populating the component with its slots, I am running into issues due to parent methods missing.
I want the components to be usable (demo) therefore I need to compensate for this.$parent not working.
<template>
<component v-bind:is="'s-' + comp.name" v-bind="props" ref="comp"> <!-- this is the corrent parent-->
<div v-if="comp.slots">
<div
v-for="(slot, i) in comp.slots"
v-bind:key="i"
v-bind:slot="slot.name"
>
<div v-if="slot.type == 'component'"> <!-- childs parent -->
<de-mo v-bind:comp="slot" /> <!-- this is the child calling a method on the parent -->
</div>
<div v-html="slot.value" v-else></div>
</div>
</div>
</component>
</template>
<script>
export default {
name: 'deMo',
computed: {
props() {
if (this.comp.props) {
return this.comp.props.reduce((a, r) => {
a[r.name] = r.value
return a
}, {})
}
}
},
props: {
comp: {
type: Object,
required: true
}
},
methods: this.$ref.comp.methods, //<-- this is an error
mounted(){
console.log(this.$ref.comp.methods)
}
},
</script>
<style></style>
1) Is there a way to copy the methods from the parent into this "demo" component via the ref attr
2) Alternatively, is there a better method to produce the same results?
Thanks
you can try to spread parent methods in a beforeCreate lifecycle as at this point your parent will be created and your component is going to register its all methods,
beforeCreate() {
this.$options.methods = { ...this.$parent.$options.methods };
},
however you can not access any refs in this as refs are only registered after mount of the component.
Note: Any library should use provide and inject to communicate with their component instead of referencing the parent component directly.
You can use an Event bus to communicate between components that aren't directly related to each other. Also, this is the recommended way of communication from child to parent in Vue.
bus.js
import Vue from 'vue'
export default new Vue()
demo.vue // child component that wants to call a method in the parent
import Bus from './bus.js'
export default {
mounted () {
// [1] child component will emit an event in the bus when it want to call a method of parent
Bus.$emit('invoke-parent-fn', 'param')
}
}
parent.vue // parent component where you want to render other components dynamically
import Bus from './bus.js'
export default {
methods: {
fn (param) {
console.log('// do something ' + param)
}
},
mounted () {
// [2] parent will be listening to the bus event, when child will emit an event, the handler passed in to the listener will be invoked
// [3] invoke the required method in the handler
Bus.$on('invoke-parent-fn', param => this.fn(param))
}
}
I want to get an "input" event from a nested component that I see firing from the Vue chrome devtools extension
I can only listen to the "input" event on the VDatePicker using the v-on directive. Is there a way to listen to the VDatePickerDateTable "input" event without extending the VDatePicker component?
<template>
<v-date-picker v-model="selectedDays" multiple #input="onDayClick"/>
</template>
<script>
export default {
methods:{
onDateClick(data){
console.log(data); // ["2019-01-01", "2019-01-02", "2019-01-06"]
}
}
}
</script>
This is what I ended up doing. Using a ref to VDatePicker and following it into a nested ref of its child, VDatePickerDateTable to listen to its "input" event. The reason I think that the input on VDatePicker passes an array of date strings is because it is a multi date picker as defined by the multiple attribute on the component. This was added in Vuetify 1.2.
It now passes the date clicked to the handler. I would love to know if anyone has a better way.
<template>
<v-date-picker ref="datePicker" v-model="selectedDays" multiple/>
</template>
<script>
export default {
mounted(){
this.$refs.datePicker.$refs.table.$on('input', this.onDayClick);
},
methods: {
onDateClick(data) {
console.log(data); // "2019-01-01"
}
}
};
</script>
I am building a form framework in vue. I have components for each field type. Each field type component uses this.$emit to communicate changes with the parent component.
I am able to trigger events in the parent component using v-on directives as follows:
<template>
<div v-if="fieldsLoaded">
<form-select :field="fields.title" v-on:updated="validate" ></form-select>
<form-input :field="fields.first_name" v-on:updated="validate" ></form-input>
</div>
</template>
However, I don't want to have to manually specify that every component should trigger the validate method individually.
How can I have the parent component listen for the updated emit across all its child components?
Edit: I'm looking for something like the below, though $on only catches emits that occur within the same component, rather than its children
created: function(){
this.$on('updated',validate)
}
The best way is to use event bus or even better in my opinion vuex.
For the first case take a look here
For the second here
With event bus you can emit an event, and listen to that event whenever you want(at parent,child even in the same component)
Vuex It serves as a centralized store for all the components in an application and you can have properties in that store,and you can use and manipulate them.
Example with event Bus:
main.js:
import Vue from 'vue'
import App from './App.vue'
export const eventBus = new Vue();
new Vue({
el: '#app',
render: h => h(App)
})
User Component
<template>
<button #click="clicked">Click me to create event</button>
</template>
<script>
import { eventBus } from './main'
export default {
name: 'User',
methods: {
clicked() {
eventBus.$emit('customEvent', 'a text to pass')
}
}
}
</script>
Admin component
<template>
<p>The message from event is: {{message}}</p>
</template>
<script>
import { eventBus } from './main'
export default {
name: 'Admin',
data: () => ({
message: ''
})
created() {
eventBus.$on('customEvent', dataPassed => {
this.message = dataPassed
}
}
}
</script>
Take a look to this tutorial to learn Vuex
For your case you can use v-model like following:
<template>
<div v-if="fieldsLoaded">
<form-select v-model="fields.title" :validate="validate" ></form-select>
<form-input v-model="fields.first_name" :validate="validate" ></form-input>
</div>
</template>
v-model is essentially syntax sugar for updating data on user input events.
<input v-model="something">
is just syntactic sugar for:
<input v-bind:value="something" v-on:input="something = $event.target.value">
You can pass a prop : value in the child components, and before changing input field call a function to validate which is also passed as a prop.
Vue.component('form-select', {
props: ['options', 'value', 'onChange', 'validate'], //Added one more prop
template: '#your-template',
mounted: function () {
},
methods: {
change (opt) {
if (this.validate !== undefined) {
var isValid = this.validate(this.value)
if(!isValid) return;
}
this.$emit('input', opt)
},
},
})