Can not get the value of id with vue $refs? - javascript

I see the about vue $ refs document, the console to see is {}, I do not know how to get the value of id?
There { } is no object, how to get $ refs id of value,but I think this {} not get to object.I try to use console.log(this.$refs.dataInfo.id) is undefined.
Look at the picture:
javascript file:
console.log(this.$refs)
HTML file:
<tab-item v-for="item in category" :id="item.id" ref="dataInfo">{{item.name}}</tab-item>

console.log(this.$refs.dataInfo.id) is undefined because you are trying to access an element's attribute through a reference to a component. Based on your example object, you can access it like this: this.$refs.dataInfo[0]['$el'].id
That's an odd way of doing it, I believe. The Vue way of doing it would be:
Using events. When props change in the child component and you want to do something about it in the parent component, you should emit a custom event. You'd do something like this in the child component: this.$emit('dataChange', data). And in the parent component add an event listener #dataChange="myMethod" to the component.
Using Vuex.
Having a good understanding of the basic concepts such as how the data flows in the application is a must and should not be overlooked!

Related

How to get Vue app container div attribute

I have a page which is being generated by my app in java (SSR) depending on some data (e.g. a publisher for some entity). I would like to create some sort of a reusable Vue component that would call an API method and request some data about the entity that is currently opened. Also in some cases there could be more than one such component on one page.
The only thing I cannot really figure out being a most-of-the-time backend developer - is how to tell a component which entity I'm trying to get. The only solution that comes to my mind is to generate the parent <div class="my-vue-component"><div> with an additional attribute, e.g. <div class="my-vue-component" publisher-id="123"><div>.
But I cannot find if there is a way to access that attribute from inside the Vue instance. (Please note that I don't have a fixed id for this div as there can be many such components on the same page referring to different data).
Any kind of advice is appreciated.
As stated in the previous answer, you will need to use props. Although since you will pass down data to multiple components and the data can change, there should be a way to respond to those changes.
For that, you will have to bind the prop with a reactive variable in your page/parent component.
So your SSR code should look like
<my-vue-component :publisher-id="openId"></blog-post>
And inside your page/parent component will reside the openId, which you can change as needed, and your component will re-render if prop passed to it changes.
export default {
data(){
return {
openId:1
}
}
}
It seems like you are looking for components-props.
You can define a prop like
Vue.component('blog-post', {
// camelCase in JavaScript
props: ['postTitle'],
Your SSR code should then be generating:
<!-- kebab-case in HTML -->
<blog-post post-title="hello!"></blog-post>
Inside the component methods you can access the passed in value using this.postTitle

vue - $emit vs. reference for updating parent data

We need to use $emit to update the parent data in a vue component. This is what has been said everywhere, even vue documentation.
v-model and .sync both use $emit to update, so we count them $emit here
what I'm involved with is updating the parent data using reference type passing
If we send an object or array as prop to the child component and change it in the child component, changes will be made to the parent data directly.
There are components that we always use in a specific component and we are not going to use them anywhere else. In fact, these components are mostly used to make the app codes more readable and to lighten the components of the app.
passing reference type values as prop to children for directly change them from children is much easier than passing values then handle emitted event. especially when there are more nested components
code readability is even easier when we use reference type to update parent.
For example, suppose we have grand-parent, parent and child components. in parent component we have a field that change first property of grand-parent data and in child component we have another field that change second property of grand-parent data
If we want to implement this using $emit we have something like this : (we are not using .sync or v-model)
// grand-parent
<template>
<div>
<parent :fields="fields" #updateFields="fields = $event" >
</div>
</template>
<script>
import parent from "./parent"
export default {
components : {parent},
data(){
return {
fields : {
first : 'first-value',
second : 'second-value',
}
}
}
}
</script>
// parent
<template>
<div>
<input :value="fields.first" #input="updateFirstField" />
<child :fields="fields" #updateSecondField="updateSecondField" >
</div>
</template>
<script>
import child from "./child"
export default {
components : {child},
props : {
fields : Object,
},
methods : {
updateFirstField(event){
this.$emit('updateFields' , {...this.fields , first : event.target.value})
},
updateSecondField(value){
this.$emit('updateFields' , {...this.fields , second : value})
}
}
}
</script>
// child
<template>
<div>
<input :value="fields.first" #input="updateSecondField" />
</div>
</template>
<script>
export default {
props : {
fields : Object,
},
methods : {
updateFirstField(event){
this.$emit('updateSecondField' , event.target.value)
},
}
}
</script>
Yes, we can use .sync to make it easier or pass just field that we need to child. but this is basic example and if we have more fields and also we use all fields in all component this is the way we do this.
same thing using reference type will be like this :
// grand-parent
<template>
<div>
<parent :fields="fields" >
</div>
</template>
<script>
import parent from "./parent"
export default {
components : {parent},
data(){
return {
fields : {
first : 'first-value',
second : 'second-value',
}
}
}
}
</script>
// parent
<template>
<div>
<input v-model="fields.first" />
<child :fields="fields" >
</div>
</template>
<script>
import child from "./child"
export default {
components : {child},
props : {
fields : Object,
}
}
</script>
// child
<template>
<div>
<input v-model="fields.second" />
</div>
</template>
<script>
export default {
props : {
fields : Object,
}
}
</script>
as you see using reference type is much easier. even if there was more fields.
now my question :
should we use reference type for updating parent data or this is bad approach ?
even if we use a component always in the same parent again we should not use this method ?
what is the reason that we should not use reference type to update parent?
if we should not use reference type why vue pass same object to children and not clone them before passing ? (maybe for better performance ?)
The "always use $emit" rule isn't set in stone. There are pros and cons of either approach; you should do whatever makes your code easy to maintain and reason about.
For the situation you described, I think you have justified mutating the data directly.
When you have a single object with lots of properties and each property can be modified by a child component, then having the child component mutate each property itself is fine.
What would the alternative be? Emitting an event for each property update? Or emitting a single input event containing a copy of the object with a single property changed? That approach would result in lots of memory allocations (think of typing in a text field emitting a cloned object for each keypress). Having said that, though, some libraries are designed for this exact purpose and work pretty well (like Immutable.js).
For simple components that manage only small data like a textbox with a single string value, you should definitely use $emit. For more complex components with lots of data then sometimes it makes sense for the child component to share or own the data it is given. It becomes a part of the child component's contract that it will mutate the data in certain circumstances and in some particular way.
what is the reason that we should not use reference type to update parent?
The parent "owns" the data and it knows that nobody but itself will mutate it. No surprises.
The parent gets to decide whether or not to accept the mutation, and can even modify it on-the-fly.
You don't need a watcher to know when the data is changed.
The parent knows how the data is changed and what caused the change. Imagine there are multiple ways that the data can be mutated. The parent can easily know which mutation originated from a child component. If external code (i.e. inside a child component) can mutate the data at any time and for any reason, then it becomes much more difficult for the parent to know what caused the data to change (who changed it and why?).
if we should not use reference type why vue pass same object to children and not clone them before passing ? (maybe for better performance ?)
Well yes, for performance, but also many other reasons such as:
Cloning is non-trivial (Shallow? Deep? Should the prototype be copied too? Does it even make sense to clone the object? Is it a singleton?).
Cloning is expensive memory- and CPU-wise.
If it were cloned then doing what you describe here would be impossible. It would be silly to impose such a restrictive rule.
#Vue Detailed usage of $refs, $emit, $on:
$refs - parent component calls the methods of the child component. You can pass data.
$emit - child components call methods of the parent component and pass data.
$on - sibling components pass data to each other.

Difficulty trying to call a method from a parent to a child in React, with the child being an AntD form component

I have a Component titled 'CollectionsPage' where I am trying to create a reference to a child, and call one of its methods via its onClick function.
The reason for this is because I want the child to handle its own state, rather than pass it all down from the parent component. The child is called 'CollectionsCreateForm' and I currently just have a function in it called 'test'. I tried to call the function but it never runs -- however, when I tried the same function from a different component, it worked. Is there something about AntD's form-within-a-modal that is causing me difficulty?
I have tried changing the reference, and I also passed in 'wrappedComponentRef' to child to ensure that wasn't the issue. I need to be able to access form in the child, but AntD says it needs to be passed in as props from the parent. I would love to put the 'onCreate' method in the child, so the modal handles everything that it should without help from the parent, but I believe it needs a reference to the formRef.
I have made an example of my issue on a sandbox. Here is the link.
When using antd to create a form you basically use the Higher-Order Component - (Form.create(Component)) that wraps your component with the test method inside.
this.testRef.current will refer to your Higher-Order Component basicly Form.create class not your own component.
You can use wrappedComponentRef like so to get reference to your own component:
<CollectionCreateForm
wrappedComponentRef={(inst) => this.formRef = inst}
/>
and then simply call this.formRef.test();
Here is a working example:
https://codesandbox.io/s/n34ppwnq9j

Access $refs from other components not in the same level as current component

I'm working on a Vue application.
It has a header and then the main content.
Nesting and structure as below
TheHeader.vue -> TheLogin.vue
MainContent.vue -> ShoppingCart.vue -> OrderSummary.vue
I need to access an element in TheLogin.vue from OrderSummary.vue
this.$refs.loginPopover.$emit('open')
gives me an error "Cannot read property '$emit' of undefined" so obviously I am not able to access $refs from other components.
The question is how do I get hold of refs from other components?
Thanks in advance!
Edit 1 - Found out $refs works with only child components.
How do I access elements across components in different level?
You definitely don't want to be reaching through the hierarchy like that. You are breaking encapsulation. You want a global event bus.
And here's a secret: there's one built in, called $root. Have your OrderSummary do
this.$root.emit('openPopup');
and set up a listener in your TheLogin's created hook:
this.$root.on('openPopup', () => this.$emit('open'));
In general, you should try to avoid using refs.
For anyone who comes here later and wants to access $refs in parent component, not in this particular case for emitting events since event bus or a store would suffice but let's just say you want to access some element in parent to get it's attributes like clientHeight, classList etc. then you could access them like:
this.$parent.$parent.$refs //you can traverse through multiple levels like this to access $ref property at the required level
You can put a function like this on your component to do this. I put mine in a Mixin:
public findRefByName(refName) {
let obj = this
while (obj) {
if (obj.$refs[refName]) {
return obj.$refs[refName]
}
obj = obj.$parent
}
return undefined
}
I also added some accessors to help:
get mycomponent() {
return this.findRefByName('mycomponent')
}
And once that exists, you can access your component by simply doing:
this.mycomponent
Thanks for that tip Abdullah!
In my case I was looking for a sibling, so in case someone comes looking for that, here's an example:
var RefName='MyCoolReferenceName';
var MyRef,x;
for(x=0;x<this.$parent.$children.length;x++)
{
if(typeof this.$parent.$children[x].$refs[RefName] !='undefined')
MyRef=this.$parent.$children[x].$refs['LABEL_'+bGroupReady.ChildID];
}
if(typeof MyRef !='undefined')
MyRef.error=true;
PS - The reason I'm doing MyRef.error=true is because I was having ZERO luck with Quasar inputs and lazy-rules="ondemand". Turns out you can just set .error=true to activate the error message and the red highlighting and .clearValidation() event to clear it back out. In case someone is trying to do that as well!

Access key from child component in vue

According to Vue docs, binding a key is required to use custom components in a v-for:
<template v-for="(task,i) in tasks">
<task-card v-bind:task="task" v-bind:key="i"></task-card>
</template>
I would like to use that key in the child component (task-card), but neither using this.key or adding key as a prop (is a reserved Vue keyword) works. Is there a way of doing this without passing yet another prop with the value "i"? Currently working with "vue": "^2.5.9".
If you want to pass data to the child, you should be using props (key is reserved so you'll have to name it something else).
Otherwise you can access the key on the vnode within the component via this.$vnode.key.
Vue 3
For Vue 3 the API has changed. You will need to access the vnode from internal private instance like so: this.$.vnode.key. As far as I know this is undocumented and may change; use with caution.

Categories

Resources