vuejs ref property has no effect when using with createElement - vue.js

I've written a custom render function for a Vue Component, but when I set the "ref" property in the data object that is passed to the createElement function, nothing shows up in the $refs of the root vm (VueComponent)
Vue.component('sm-form-row', {
render: function (createElement) {
// Create the Row Div and append the columns
return createElement('div', {
class: {
'row': true
},
ref: 'some computed value'
});
}
});
What am I missing, the class is being applied correctly but the $refs keep showing empty.

The ref is beign applied and i made a fiddle to see it that it works.
But,if you want to add a reference to sm-form-row component then you have to add the ref attribute in the parent component.For example in parent component:
<sm-form-row ref="formRow" />
And in your parent component you can access it as:
this.$refs.formRow
Also you will be able to access the methods of the child component.For example if the child component has a method called myMethod you can access it in parent component like this:
this.$refs.formRow.myMethod

Related

How to watch child properties changes from parent component

I am using a date picker component library and i want to watch when a property of that component changes.
I have tried this:
watch: {
'$refs.picker.popupVisible': {
handler (new_value) {
console.log('executed')
},
deep: true
}
}
Also this:
computed: {
picker () {
console.log(this.$refs.picker.popupVisible)
return this.$refs.picker.popupVisible
}
}
I know that the solution will be a vue.js hack because this is not the right way.If i had access to child component i would emit en event to parent but unfortunately i don't have.
I had a similar problem using a library which had some limitations.
Unfortunately, your watcher will not work.You have to use the function watcher to make this to work.And you have to use it inside the mounted hook.
mounted() {
this.$watch(
"$refs.picker.popupVisible",
(new_value, old_value) => {
//execute your code here
}
);
}
I also have an example. Please take a look here
What you can do is create a data object in parent component and include the date field in that data object and pass that data object to child component as props
<child :dateObj="dateObj"></child>
data: {
dateObj: {
date: ""
}
}
And in child component you can use the date field of that dateObj props. This is possible because Vue doesn't watch the property of Objects passed as props and we can modify them without Vue complaining in console.
Thus the changed date field is reflected in parent as well.

vuejs pass model from parent component to children component and allow mutation

Currently I have a vue-multiselect component which requires a v-model.
I want to wrap this component so that I can build one single-select component and one multi-select component.
While working on the single select component I encountered the following warning
[Vue warn]: Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop's value. Prop being mutated: "model"
They are right but in my case I really need to change the value from the parent (like I replace my single-select code with the vue-multiselect code) component and I also do not want this warning.
Here is the code for my component:
Vue.component('single-select', {
props: {
model: {
required: true
}
}
template: '<multiselect\n' +
' v-model="model"\n' +
...>\n' +
...
'</multiselect>'
});
One solution would be to pass a function as a model parameter and return the field from the parent but I really hope for a better solution.
Vue has a shortcut for 2 way binding called .sync modifier.
How it works in your case:
add .sync when you pass model as prop
<single-select :model.sync="..."></single-select>
emit an update:model in the child's input event
Vue.component('single-select', {
props: {
model: {
required: true
}
},
template: `<multiselect :value="model" #input="$emit('update:model', $event)"> </multiselect>`
});
Just give the internal model reference a different name, and the in the Vue component's data function map it manually:
Vue.component('single-select', {
props: {
model: {
required: true
}
},
data: function() {
return {
singleSelectModel: this.model
};
}
template: '<multiselect v-model="singleSelectModel"></multiselect>';
});
This is of course, assuming that you do not want to mutate the parent data, but simply making a copy of model and giving the child component the freedom to change it whenever it wants.
If what you want is to also update the parent data from the child, you will have to look into emitting events from the child and listening in the parent.

Passing data to Vue.js component

I am creating a component and want to pass two properties (item & brokerageID) to the component. Here is the HTML code:
{{brokerageID}}
<holiday-component v-bind:item="item" v-bind:brokerageID="brokerageID" testID="45" ></holiday-component>
Here is the code for 'holiday-component'
Vue.component('holiday-component', {
props: ['item',
'brokerageID',
'testID',
],
data () {
return {
holidaysData: [],
showHolidays: false,
}
},
methods: {
getHolidays(contactID) {
....
},
template: <div> {{testID}} {{item.contactName}} {{brokerageID}}
....
The 'item' property is getting passed to the component (item.contactName is displayed correctly in the component template. However, somehow, brokerageID (property of the Vue object) is not getting passed. This property exists which is confirmed as {{brokerageID}} used above the component in HTML displays value. But, within the component template, brokerageID is not available. Also, the testID property passed to the component is not displayed.
Could someone please advise, what is wrong in my implementation that I am unable to use brokerageID in my component?
See Vue's docs about prop naming https://v2.vuejs.org/v2/guide/components.html#camelCase-vs-kebab-case
In this instance, using v-bind:brokerage-id and v-bind:test-id should do the trick.

How can I get a child component's watcher's updated value from parent component in Vue 2?

I have two components that are in a parent-child relationship. In the child select component I've defined a watcher for the currently selected value and I want the parent to get the updated value whenever it changes. I tried to use events but the parent can only listen to its own vm.
In the child component
watch: {
selected (value) {
this.$emit('selectedYearChange', value)
}
}
In the parent
mounted () {
this.$on('selectedYearChange', function (value) {
this.selectedYear = value
})
}
This obviously doesn't work because of the reason I mentioned above.
I wasn't able to find relevant examples in the documentation.
You can setup an event listener on the child component element itself where its used in the parent and pass it a method to be executed
//parent component
<div id="app>
<child-comp #selected-year-change='changeYear'></child-comp>
</div>
In your parent component use the method changeYear to change the value of `selectedYear'
methods:{
//you get the event as the parameter
changeYear(value){
this.selectedYear = value
}
}
See the fiddle
And avoid using camelCase for event names use kebab-case instead as HTML attributes are case-insensitive and may cause problems
html:
<Parent>
<Child #selected-year-change=HandleChange(data)></Child>
</Parent>
Javascript/Child Component:
watch: {
selected (value) {
this.$emit('selectedYearChange', value)
}
}
Javascript/Parent Component:
HandleChange: function(data){}
good luck. maybe work!

how can component delete itself in Vue 2.0

as title, how can I do that
from offical documentation just tell us that $delete can use argument 'object' and 'key'
but I want delete a component by itself like this
this.$delete(this)
I couldn't find instructions on completely removing a Vue instance, so here's what I wound up with:
module.exports = {
...
methods: {
close () {
// destroy the vue listeners, etc
this.$destroy();
// remove the element from the DOM
this.$el.parentNode.removeChild(this.$el);
}
}
};
Vue 3 is basically the same, but you'd use root from the context argument:
export default {
setup(props, { root }){
const close = () => {
root.$destroy();
root.$el.parentNode.removeChild(root.$el);
};
return { close };
}
}
In both Vue 2 and Vue 3 you can use the instance you created:
const instance = new Vue({ ... });
...
instance.$destroy();
instance.$el.parentNode.removeChild(instance.$el);
No, you will not be able to delete a component directly. The parent component will have to use v-if to remove the child component from the DOM.
Ref: https://v2.vuejs.org/v2/api/#v-if
Quoted from docs:
Conditionally render the element based on the truthy-ness of the expression value. The element and its contained directives / components are destroyed and re-constructed during toggles.
If the child component is created as part of some data object on parent, you will have to send an event to parent via $emit, modify (or remove) the data and the child component will go away on its own. There was another question on this recently: Delete a Vue child component
You could use the beforeDestroy method on the component and make it remove itself from the DOM.
beforeDestroy () {
this.$root.$el.parentNode.removeChild(this.$root.$el)
},
If you just need to re-render the component entirely you could bind a changing key value to the component <MyComponent v-bind:key="some.changing.falue.from.a.viewmodel"/>
So as the key value changes Vue will destroy and re-render your component.
Taken from here