How to get the parent template component in Vue - vue.js

I know in vue, I can use this.$parent to get the upper component in the vdom tree. But I'm expecting something different: to get the component that rendered the current component.
For instance, I have a component (named comp-container) with template:
<template>
<comp-a>
<comp-b></comp-b>
</comp-a>
</template>
And in comp-b the $parent would be an instance of comp-a not comp-container which I'm expecting.
My current aproach is traversing up with the $parent attribute until I find comp-b exists in $options.components. This method is working for now but seems quite ugly and breaks if comp-b is a globaly registered component. Is there an official way to do this?
Passing the parent template component via props as <comp-b :container="this"></comp-b> may do the job, but it's too verbose to be liked.

I'm not sure about the exact use case, but basically if there are slots involved (which I almost assume, because otherwise $parent will work fine), you can find the rendering component at:
this.$slots.default[0].context;
Basically, the context property of a slot is the rendering context (rendering component - i.e. the component who's template the component was rendered in).
Only tested with Vue 2

Related

Vue: watching a v-model's 'value' in a component

I have this component which has a v-model directive:
<my-component v-model="someData.someProp"></my-component>
Now, I'd like to be able to watch this piece of data inside the component and make changes based on this model changing via outside influences. So here's what I tried and it's not working:
watch : {
value (newVal, oldVal) {
// ...
}
}
It seems like it should work, or something comparable should be out there, but I just can't find it right now.
EDIT:
The most common answers I found and provided center on using the watcher to watch the data as if it's inside the parent component - but I'd like to watch it inside the child component without concern of what's going on in the parent.
I'm able to work around this by not using v-model and using simple named properties such as :my-data="someData.someProp" at which point I can successfully watch a myData variable inside the child component. I can also use #input to set the data back in the parent component if it's changed from within. But v-model is shorter and if there's a way to use that instead of a workaround that would be preferable.
From what I understand you are trying to pass a prop to your child component from your parent one and watch it in your child.
Assuming you have something like this in your parent:
<div>
<my-component v-model="someData.someProp"></my-component>
</div>
I understand you are getting undefined for that prop when you watch it, which is normal because you should pass it like this:
<my-component :myValue="someData.someProp"></my-component>
Then you should have access to the prop trought this.myValue.
If someData.someProp changes on the parent component it will automatically reflect on the child one.
Again this is what I could understand from your explanation and the amount of code you provided.

VueJS: Child - Parent Component update

I am currently working on a project that has a component structure like follows
Parent component that contains items
This component has a v-for over this.items.
That v-for has a
ChildComponent contains a form that modifies properties of this.item via v-model for each property.
I was expecting, since VueJS does not support 2 way databinding by default, that ParentComponent would not be aware about changes that I make in ChildComponent.
However, since my ChildComponent is a modal dialog, I can see the changes that I make in ChildComponent are reflected in ParentComponent.
As much as I like what I see, I don't understand it...
Could someone shed a light on this for me?

Default-expand-all doesn't work for q-tree? Vue.Js

I have to code a web application and the most important element is the q-tree. I'm already able to load and show data (passing an array called list), but I want that all nodes are expanded.
The vue.js examples of the official documentation show that you're be able to do this with the 'default-expand-all' attribute but this isn't working for me.
It only shows me the root node with an arrow, where I have to expand the children nodes manually.
<q-tree
:nodes="list"
:selected.sync="selected"
#update:selected="onSelectionChangedNode"
node-key="NodeNr"
label-key="NodeTxt"
default-expand-all
></q-tree>
Taking a cue from the accepted answer, I realised that the dom has already been created with the tree component on first render.
In my use case, I want to update the Tree when data comes back from the server.
So, I had to force it to re-render with the expanded functionality using:
this.$nextTick(function () {
this.$refs.nodes.expandAll();
})
The nextTick function will update the dom in the next window of execution, by which time the nodes will get expanded by calling the expandAll function.
And NB: For those confused by the astericks on the ref attribute or how to add it to the component, here goes:
<q-tree :nodes="list"
:selected.sync="selected"
#update:selected="onSelectionChangedNode"
node-key="NodeNr"
label-key="NodeTxt"
ref="nodes"
>
Solved my problem as following:
I have added a ref attribute to the QTree DOM Element which makes it possible to access predefined methods of QTree API.
<q-tree
:nodes="list"
:selected.sync="selected"
#update:selected="onSelectionChangedNode"
node-key="NodeNr"
label-key="NodeTxt"
**ref="nodes"**
>
The function I have been using is expandAll().
updated() {
this.$refs.nodes.expandAll();
}
The most important thing for me was, I had to find out which lifecycle hook was the right one for me. The update() hook was the one I was looking for.
The reason:
Called after a data change causes the virtual DOM to be re-rendered and
patched.
The component’s DOM will have been updated when this hook is called, so you
can perform DOM-dependent operations here.
The default-expand-all is only applied on the first rendering of that Component.
So if your Component renders when the nodes aren't assigned they wont expand if assigned afterwards.
https://v1.quasar-framework.org/vue-components/tree
You have to work with scoped slots and an expanded attribute if you dont have the nodes on first rendering.

Vue component 'mount' on v-bind change

mount on a component does not fire for v-bind changes to the component in the root.
I have a dynamic component where I often change the value of the data referenced in v-bind:is and v-bind attributes, switching the current component and its data. This works great to show different "screens" of my application.
I have some logic that happens on mount for many of the components. However, this only gets called when the v-bind:is value is changed, but no when only the v-bind attribute is. That sort of makes sense to me, though I do need to capture either.
I'm new to Vue and there is a lot of documentation. I'm so far unable to determine if there's a built-in functionality for a callback to v-bind:is and v-bind. Can anyone help me out here?

Does $destroy function removes the Vue Custom component from cache

I construct deep nested tree of parent and children Vue custom components using my top level component dynamically and then I am updating the data from which all tree is constructed. Which has an effect of rendering the entire tree (its a form with various custom components). I refresh/rebuild the whole form after fetching the data (which is what vue do for reactive data) that itself tell me how to regenerate the view (its like a JSON Schema from which I render the entire view).
This is related to my other issue here.
I am observing a very weird behavior in my Vue Application. When I destroy all my children components and rebuild the data to force rendering the form, it appears that even after I have called $destroy on every child component...Vue is not entirely removing them from cache?
Does vue remove the component from cache if a $destroy is called ?
Because I do not see multiple components of the same type in the Vue component list in the Chrome Vue DevTool extension panel. But I see that the same custom event is handled twice by the same component. Same function that handle the events is getting called twice even though there is only one component visible in Vue DevTools of this type.
This only happens after I render the form. When the page is loaded for the first time every thing works. Then after I reset the form by destroying the child component and resetting the data to re-render the form, magically this child component start handling the event twice.. and in 3rd render it handle the events thrice. But I see only one component in google chrome VueJS DevTool extension panel. So my guess is that vue is still keeping the previously destroyed component in cache. I am trying to figure out how should I destroy those components in the cache.
If anyone has observed something similar and found a solution please let me know.
At the moment I am going to dig little bit more on my component keys (this particular component does not have explicit key set by me).
First and foremost, the vue documentation states:
vm.$destroy
In normal use cases you shouldn’t have to call this method yourself.
Prefer controlling the lifecycle of child components in a data-driven
fashion using v-if and v-for.
So instead of destroying and rebuilding the components manually yourself, you should really letting vue handle that via v-if and v-for. If the components aren't updating to your changes, you might be dealing with a reactivity issue.
As you mentioned that this is a deeply nested structure, the reactivity is key to keeping the components up to data with the data.
Vue does not allow dynamically adding new root-level reactive properties to an already created instance. However, it’s possible to add reactive properties to a nested object using the Vue.set(object, key, value) method:
Vue.set(vm.someObject, 'b', 2)
Inside of a component:
this.$set(this.someObject, 'b', 2)
Also, keep in mind that Vue should be doing the heavy lifting in regards to component management, while you should define the parameters by which a component is rendered.