Get width of parent element in Vue js - javascript

I am trying to implement a component named CamViewMatrix in Vue.js. My requirement is to get the width of CamViewMatrix's parent inside itself (say in created() method of CamViewMatrix) so that I can make some calculations with it (there are some time-based charts that need to be made responsive which is not possible unless an initial value for width is in hand).
<div>
<div class='controller-wrapper'>
...
</div>
<CamViewMatrix v-bind:cameras='cams' />
</div>
I tried assigning an id to its parent and passed the same as a prop to CamViewMatrix, and attempts to getElementById, but it's not able to. Code given below:
<div id='parentid'>
<div class='controller-wrapper'>
...
</div>
<CamViewMatrix v-bind:cameras='cams' parentId='parentid' />
</div>
And inside CamViewMatrix component:
<script>
export default {
name: 'CamViewMatrix',
props: {
parentId: {
type: String,
required: true,
},
...
},
created() {
console.log(document.getElementById(this.parentId)); // 👈️ logs null
...
}
}
With the above code, I am not able to get the parent element (it's logging null).
Have also tried using ref and $parent, but was not able to. Kindly help me with this.
Thanks in advance.

created() is the wrong lifecycle hook to do that.
If you check the lifecycle diagram from the docs: https://v2.vuejs.org/v2/guide/instance.html#Lifecycle-Diagram
The element stuff is not yet done (rendered) and only the instance is created.
You most likely what to do that in mounted.
Next, you can access your component's parent via this.$parent and the element via this.$parent.$el.
Check out: https://v2.vuejs.org/v2/api/#Instance-Properties
If you want a specific element from parent, create a ref and access it like:
this.$parent.$refs.refNameYouAdded
If it's a Vue component, then it'd be a Vue instance so you need to access $el again:
this.$parent.$refs.refNameYouAdded.$el
otherwise if it's a HTML element, then no need to do that.
most of the time, the above answers work but sometimes if your parent component does things like moving elements somewhere else then it might not be accurate.
as mentioned in the comment, you can still access the correct parent element by doing:
this.$el.parentElement

Related

Using the same component in a child and parent in Vue

I have a VueJS project - which in one view there are parent and child components that are both using the same component called deleteModal.
When I trigger the modal from the child (to show it), it triggers both the child and parent modals (except no data is passed to the modal triggered by the parent). As I understand it, this is because I have used the component twice - in the parent and child - example below. To note, it works as expected from the paren
I've researched and tried a few things: setting a key value for each of the components, changing the components ref name among other things. I have also tried using v-show to only show component just before I trigger the parent model however, this solution is not ideal.
How can I only trigger the modal from the child?
Parent Component
<template>
<div>
<childCompt ref="childCompt" />
<deleteModal
ref="deleteModal"
#deleteTriggerAPI="deleteAPIParent"
/>
</div>
<template>
Child Component - childCompt
<template>
<div>
<deleteModal
ref="deleteModal"
#deleteTriggerAPI="deleteAPIChild"
/>
</div>
<template>
My old answear is not good at all. I personally to show and hide element using jquery in vue. For me right now this is best solution but maybe i don't know some best.
If you want use only vue i using also variable passing to child from parent which will support visable of your modal.
We pass variable with ":" and register event with "#".
<template>
<childComponent :isModalOpen="isModalOpen" #onModalClose="isModalOpen=false">
<template>
export default {
name:"parent",
data: () => {
isModalOpen: false
}
}
In child we catch this by using props. We need to define type of varialbe we pass. Different between props and data is that in props we cannot change value in child component.
export default {
name: "child",
props: {
isModalOpen: Boolean
}
}
And you can use this variable to show or hide modal. Also in child component we can create button to close modal and we emit event to parent in order to change variable value.
To do this we using this.$emit('eventName');
More information right here: https://forum.vuejs.org/t/passing-data-back-to-parent/1201
You could try globally defining the component,
ie, in main.js
Vue.component('deleteModal',deleteModal)

Vue template: How to automatically add custom attribute to elements that have a v-on:click directive

We are using single file vue components and in a mousemove event handler we'd like to be able to detect if the target element is clickable.
In our Vue templates we are using v-on directives: v-on:click="someCallback".
Unfortunately there doesn't seem to be an easy way to tell for a given element if an event listener was registered for it (i.e. via v-on directive).
For this we'd like to add a custom attribute to those elements with a v-on:click directve - i.e. "clickable". But this should happen automatically.
So we'd have to either wrap Vue's own "on"-directive into a custom one or somehow hook into Vue's rendering cycle - but this seems not very straight-forward: Couldn't find the directive on the Vue instance or the Vue component object.
What we have tried:
retrieving the information about registered listeners from the target element provided by the event object passed to the event handler. But apparently browsers don't provide this information.
searching the Vue component object for some object that stores information about which event listener has been registered for which element with which handler. We were not able to find this information - but it should be somewhere, right?
Hope anyone has a nice idea on how to accomplish adding a custom attribute to elements with v-on:click directive automatically.
Thanks!
EDIT:
So we have i.e.
<div id="x" #click="someMethod" />
in our template.
But we want to add a custom attribute automatically (we dont want to add it manually for all the trillion cases):
<div id="x" clickable #click="someMethod" />
Then in the event handler for addEventListener('mousemove', handler) we would check for this attribute: if (e.target.hasAttribute('clickable'))
But any other way of accomplishing this (so being able to tell inside the handler for mousemove if the element is clickable) would be fine too.
You could create a container component and import it into all your other vue components, ensuring it's the first component in your template, like:
<template>
<v-container>
// your template here
</v-container>
</template>
<script>
// Obviously replace the path and point to your location of the component
import ComponentContainer from './common/ComponentContainer.vue'
export default {
name: 'MyClientComponent',
components: {
'v-container': ComponentContainer
}
}
</script>
And this is the container component that looks for click events and adds the clickable class:
<template>
<div class="component-container">
<slot></slot>
</div>
</template>
<script>
export default {
name: 'ComponentContainer',
mounted() {
this.$slots.default.forEach(vn => {
this.addClickableClassNames(vn);
});
},
methods: {
addClickableClassNames(vnode) {
if (vnode) {
let data = vnode.data;
if (data && data.on) {
// Check for click events and add a
// clickable class if one exists
if (data.on.click && vnode.elm && vnode.elm.classList) {
vnode.elm.classList.add('clickable');
}
}
// Now recursively check children
if (vnode.children) {
vnode.children.forEach(vn => {
this.addClickableClassNames(vn);
});
}
}
}
}
}
</script>
This works, but I wouldn't like to comment on the performance of a large dom. And you, and other devs, need to remember to import into all components, which isn't ideal. But, it's a solution that might give you other ideas on improving and making more scalable.
I can't think of a way to "automatically" add this clickable attribute. I think unfortunately you will still need to "tag" your clickable elements one by one.
I would have a directive which you can add to any element in your templates.
Directive
Vue.directive('myDirective', {
inserted(el, bindings) {
el.addEventListener('mouseover', () => {
alert(bindings.value);
})
}
});
Usage
<span v-my-directive="true">Element 1</span>
<span v-my-directive="false">Element 2</span>
You will notice that in the template when using the directive a value is being passed to it. This is then read via bindings.value. Of course based on this value you can do whatever functionality you need.
JSFiddle: https://jsfiddle.net/tc45xf82/2/
Vue Docs: https://v2.vuejs.org/v2/guide/custom-directive.html

React - using "document" inside of a component

If i have a presentational (child) component with the following line:
<small id="parameters"> some parameters text </small>
I noticed that if i go to its parent container component and try to get that element by:
const textElem = document.getElementById('parameters').value;
It is not going to get its value.
Why is that? If i use document in a component, is it only "local" to that component?
You can use document inside a component, to reference the global document object, it's not local to that component, however, at the point you're referencing document.getElementById('parameters') this element might not have been rendered yet to the dom, so make sure to call it after the child element has rendered.
On the other hand, maybe you meant to use: document.getElementById('parameters').innerHTML
to get the text inside that element, instead of .value

Polymer 2.0 access a parent component from child component

I have a nested component, what is the Polymer way to travel up the DOM or component tree in this case.
<parent-component>
<some-component>
<component-i-am-starting-from></<component-i-am-starting-from>
</some-component>
<some-other-component>
</some-other-component>
</parent-component>
I'd like to be at a deep nested component and reference any of the parent components and their models or trigger an event inside one of them. Bonus if I can access sibling components, etc.
Traveling down was easy enough with
this.$.idOfChildComponent.event()
I have tried dispatchEvent, domHost, shadowRoot, can't seem to get any further up the component tree then the direct parent or get an error back that something is undefined, is not a function, etc.
Is there a way like React to pass a reference down as a property. The docs do not seem to be helpful nor scouring the internet.
Thanks!
update
So I am not sure if this is the correct way but it works ok calling a parent function from a child function.
<parent-component id="parentComponent">
<some-component>
<component-i-am-starting-from></<component-i-am-starting-from>
</some-component>
<some-other-component>
</some-other-component>
</parent-component>
componentIAmStartingFromFunc(){
document.getElementById('parentElement').parentElementFunc()
}
However does not seem to work for siblings?
** Update **
I essentially did the same the to call the sibling event by calling the parent from one of its children, and then sent out a trigger to the sibling which is also a child component.
Calling a parent function from child ;
child-component.html (polymer 2.x)
this.dispatchEvent(new CustomEvent('upway-func', {detail: {op:"Optionally I can send some data"}}));
child-component.html (polymer 1.x)
this.fire('upway-func', {detail: {op:"Optionally I can send some data"}});
parent-component.html
...
<child-component on-upway-func='_someFunction'></child-component>
...
_someFunction(d){
console.log(d.detail.op); // "Optionally I can send some data"
}
Here this link for more detail
Have you tried using this.parentElement?
Ask yourself: if I were a span in a div, how would I get to the div?
If you want to find a particular element in the ancestor chain by selector, you can use .closest() in good browsers or https://github.com/jonathantneal/closest
The message system mentioned in another answer can work but doesn't help you with positional relationship traversing which I assume your question was requiring. Passing ids and/or objects down can be used with messages in some cases.

Get parent div in React Component

I have a table within a bootstrap panel.
Something like this:
<Panel className='userPanel'>
<Table/>
</Panel>
Now I need to have a reference to Panel within Table. There can be a lot of tables/panels on one site the same time, so I don't want to use ids to use them with ReactDom.findDOMNode.... What is the best solution for this?
Edit:
I need a reference in the table because I use Facebooks datatable. These tables do have a static width/height so to make them fluid, I need to add a resize handler that checks the width and height of the parent to adjust.
What you are suggesting breaks the way that React encourages you to work. Components should be encapsulated, all the data they need should be passed to them as props. Every time you make calls out from a component you invalidate their referential transparency i.e. the output of a component (usually what is rendered) should be the result of the props passed to it.
Having said that, people use React in different ways (it even gives you setState to wreak havoc with purity) however, passing the actual DOM node to children is difficult as when they are instantiated their parents' DOM node does not exist.
I dont know the Facebook DataTable API so I can't really suggest a better way of restructuring your app, but it sounds like you just need a dynamic width/height to size the table so pass down a function to get that.
class Parent extends React.Component {
constructor( props ) {
super( props )
}
getDimensions() {
// Do some stuff here to return dimensions
return {
width: XXX,
height: XXX
}
}
render() {
<Parent ref="el">
<Table getDimension={ this.getDimensions } />
</Parent>
}
}
You still can't call this.props.setDimensions from within Table:render but you cant with the .parentNode solution anyway so I'm guessing you’re already rendering the table in componentDidMount (or later).

Categories