Call a function from another component using composition API - vue.js

Below is a code for a header and a body (different components). How do you call the continue function of the component 2 and pass a parameter when you are inside component 1, using composition API way...
Component 2:
export default {
setup() {
const continue = (parameter1) => {
// do stuff here
}
return {
continue
}
}
}

One way to solve this is to use events for parent-to-child communication, combined with template refs, from which the child method can be directly called.
In ComponentB.vue, emit an event (e.g., named continue-event) that the parent can listen to. We use a button-click to trigger the event for this example:
<!-- ComponentB.vue -->
<script>
export default {
emits: ['continue-event'],
}
</script>
<template>
<h2>Component B</h2>
<button #click="$emit('continue-event', 'hi')">Trigger continue event</button>
</template>
In the parent, use a template ref on ComponentA.vue to get a reference to it in JavaScript, and create a function (e.g., named myCompContinue) to call the child component's continueFn directly.
<!-- Parent.vue -->
<script>
import { ref } from 'vue'
export default {
⋮
setup() {
const myComp = ref()
const myCompContinue = () => myComp.value.continueFn('hello from B')
return {
myComp,
myCompContinue,
}
},
}
</script>
<template>
<ComponentA ref="myComp" />
⋮
</template>
To link the two components in the parent, use the v-on directive (or # shorthand) to set myCompContinue as the event handler for ComponentB.vue's continue-event, emitted in step 1:
<template>
⋮
<ComponentB #continue-event="myCompContinue" />
</template>
demo
Note: Components written with the Options API (as you are using in the question) by default have their methods and props exposed via template refs, but this is not true for components written with <script setup>. In that case, defineExpose would be needed to expose the desired methods.

It seems like composition API makes everything a lot harder to do with basically no or little benefit. I've recently been porting my app to composition API and it required complete re-architecture, loads of new code and complexity. I really don't get it, seems just like a massive waste of time. Does anyone really think this direction is good ?

Here is how I solved it with script setup syntax:
Parent:
<script setup>
import { ref } from 'vue';
const childComponent = ref(null);
const onSave = () => {
childComponent.value.saveThing();
};
</script>
<template>
<div>
<ChildComponent ref="childComponent" />
<SomeOtherComponent
#save-thing="onSave"
/>
</div>
</template>
ChildComponent:
<script setup>
const saveThing = () => {
// do stuff
};
defineExpose({
saveThing,
});
</script>
It doesn't work without defineExpose. Besides that, the only trick is to create a ref on the component in which you are trying to call a function.
In the above code, it doesn't work to do #save-thing="childComponent.saveThing", and it appears the reason is that the ref is null when the component initially mounts.

Related

vue3: extends missing parent's setup

parent.vue
<template>
<input :type="computedType"/>
</template>
<script setup>
import { ref, computed } from 'vue';
const props = defineProps({
type: {
required: false,
type: String,
default: 'text',
},
});
const showPassword = ref(false);
const computedType = computed(() => {
if (props.type === 'password') {
return showPassword.value ? 'text' : 'password';
}
return props.type;
});
</script>
<script>
export default {
data() {
return {
uuid: getRandomUuid(),
}
}
}
</script>
child.vue
<template>
<input :type="computedType"/>
</template>
<script>
import Parent from '~/components/parent.vue'
export default {
extends: Parent
}
</script>
In Vue3, I have a child.vue component which is inherited from parent.vue, I defined a computedType computed attribute in parent.vue, but it's missing in child.vue, however, uuid which is defined in parent.vue is accessible in child.vue.
[Vue warn]: Property "computedType" was accessed during render but is not defined on instance.
How to get computedType and any other attributes defined in <script setup> of parent.vue in child.vue?
Really appreciate any help provided!
update:
Only if I define all the attributes in <script>(but not in setup()) instead of <script setup>, they could be accessible
There are a few specific instances where you can have more than one script tag, and they are all outlined here in the documentation. Still, besides those 3 specific use cases, you shouldn't use more than one script tag. Your case of a separate script tag for one data variable is not a valid use case.
I recommend writing the component entirely with the options API (the only API that supports the "extends" option), or writing entirely with the composition API where you would use composable functions to effectively extend your component
defineExpose({computedType});
try this

Vue3 (Vite) directly access parent data from child component

I have a simple landing page using Vue3 Vite (SSG) without Vuex.
I need to pass a screenWidth value being watched in App.vue to a bunch of child components so that they change images depending on the user's screenWidth.
I could use props to pass this value, but it seems a bit cumbersome to write them for 8 child components, and to use composition data export or provide / inject is definitely overkill.
is there not a way to simply access a parent's data via something like instance.parent (didn't work), $parent.message (Vue2 way), etc from a child component?
// Parent:
data() {
return {
screenWidth: 123
}
}
// Child
<div v-if="$parent.screenWidth > 1200">
img...
</div>
EDIT: Solving this with props for now as no other (working) solution seems to be available in Vite for what used to be easy as pie in Vue2.
EDIT 2: It occurs to me now that using VueUse's built in useWindowSize might have been a good solution here.
Use v-model binding.
Parent component (assuming setup script):
<script setup lang='ts'>
import {ref} from 'vue';
const screenWidth = ref(720);
// use screenWidth as a regulat reactive variable here
</script>
<template>
<Child v-model="screenWidth" />
</template>
Child component:
<script setup lang="ts">
import {ref, watchEffect} from 'vue';
const props = defineProps<{
modelValue: number;
}>();
const emit = defineEmits<{
(e: 'update:modelValue', value: number): void
}>();
const value = ref(props.modelValue);
watchEffect(() => value.value = props.modelValue);
function setValue(newValue: number) {
value.value = key;
emit('update:modelValue', value.value);
}
</script>
<template>
// Use `value` and `setValue` here
</template>

can't use template ref on component in vue 3 composition api

I want to get the dimensions of a vue.js component from the parent (I'm working with the experimental script setup).
When I use the ref inside a component, it works as expected. I get the dimensions:
// Child.vue
<template>
<div ref="wrapper">
// content ...
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
const wrapper = ref(null)
onMounted(() => {
const rect = wrapper.value.getBoundingClientRect()
console.log(rect) // works fine!
})
</script>
But I want to get the dimension inside the parent component. Is this possible?
I have tried this:
// Parent.vue
<template>
<Child ref="wrapper" />
</template>
<script setup>
import Child from './Child'
import { ref, onMounted } from 'vue'
const wrapper = ref(null)
onMounted(() => {
const rect = wrapper.value.getBoundingClientRect()
console.log(rect) // failed!
})
</script>
the console logs this error message:
Uncaught (in promise) TypeError: x.value.getBoundingClientRect is not a function
In the documentation I can only find the way to use template refs inside the child component
does this approach not work because the refs are "closed by default" as the rfcs description says?
I ran into this issue today. The problem is that, when using the <script setup> pattern, none of the declared variables are returned. When you get a ref to the component, it's just an empty object. The way to get around this is by using defineExpose in the setup block.
// Child.vue
<template>
<div ref="wrapper">
<!-- content ... -->
</div>
</template>
<script setup>
import { defineExpose, ref } from 'vue'
const wrapper = ref(null)
defineExpose({ wrapper })
</script>
The way you set up the template ref in the parent is fine. The fact that you were seeing empty object { } in the console means that it was working.
Like the other answer already said, the child ref can be accessed from the parent like this: wrapper.value.wrapper.getBoundingClientRect().
The rfc has a section talking about how/why this works: https://github.com/vuejs/rfcs/blob/master/active-rfcs/0040-script-setup.md#exposing-components-public-interface
It's also important to note that, with the <script setup> pattern, your ref in the parent component will not be a ComponentInstance. This means that you can't call $el on it like you might otherwise. It will only contain the values you put in your defineExpose.
I don't this this is necessarily related to the <script setup> tag. Even in the standard script syntax your second example will not work as-is.
The issue is you are putting ref directly on the Child component:
<template>
<Child ref="wrapper" />
</template>
and a ref to a component is NOT the same as a ref to the root element of that component. It does not have a getBoundingClientRect() method.
In fact, Vue 3 no longer requires a component to have a single root element. You can define your Child component as :
<template>
<div ref="wrapper1">// content ...</div>
<div ref="wrapper2">// content ...</div>
</template>
<script >
import { ref } from "vue";
export default {
name: "Child",
setup() {
const wrapper1 = ref(null);
const wrapper2 = ref(null);
return { wrapper1, wrapper2 };
},
};
</script>
What should be the ref in your Parent component now?
Log the wrapper.value to your console from your Parent component. It is actually an object of all the refs in your Child component:
{
wrapper1: {...}, // the 1st HTMLDivElement
wrapper2: {...} // the 2nd HTMLDivElement
}
You can do wrapper.value.wrapper1.getBoundingClientRect(), that will work fine.
You could get access to the root element using $el field like below:
<template>
<Child ref="wrapper" />
</template>
<script setup>
import Child from './Child'
import { ref, onMounted } from 'vue'
const wrapper = ref(null)
onMounted(() => {
const rect = wrapper.value.$el.getBoundingClientRect()
console.log(rect)
})
</script
Right, so here's what you need to do:
// Parent component
<template>
<Child :get-ref="(el) => { wrapper = el }" />
</template>
<script setup>
import Child from './Child.vue';
import { ref, onMounted } from 'vue';
const wrapper = ref();
onMounted(() => {
const rect = wrapper.value.getBoundingClientRect()
console.log(rect) // works fine!
});
</script>
and
// Child component
<template>
<div :ref="(el) => { wrapper = el; getRef(el)}">
// content ...
</div>
</template>
<script setup>
import { defineProps, ref, onMounted } from 'vue';
const props = defineProps({
getRef: {
type: Function,
},
});
const wrapper = ref();
onMounted(() => {
const rect = wrapper.value.getBoundingClientRect()
console.log(rect) // works fine!
});
</script>
To learn why, we need to check Vue's documentation on ref:
Vue special-attribute 'ref'.
On dynamic binding of (template) ref, it says:
<!-- When bound dynamically, we can define ref as a callback function,
passing the element or component instance explicitly -->
<child-component :ref="(el) => child = el"></child-component>
Since the prop lets you pass data from the parent to a child, we can use the combination of the prop and dynamic ref binding to get the wanted results. First, we pass the dynamic ref callback function into the child as the getRef prop:
<Child :get-ref="(el) => { wrapper = el }" />
Then, the child does the dynamic ref binding on the element, where it assigns the target el to its wrapper ref and calls the getRef prop function in that callback function to let the parent grab the el as well:
<div :ref="(el) => {
wrapper = el; // child registers wrapper ref
getRef(el); // parent registers the wrapper ref
}">
Note that this allows us to have the ref of the wrapper element in both the parent AND the child component. If you wished to have access to the wrapper element only in the parent component, you could skip the child's callback function, and just bind the ref to a prop like this:
// Child component
<template>
<div :ref="getRef">
// content ...
</div>
</template>
<script setup>
import { defineProps } from 'vue';
const props = defineProps({
getRef: {
type: Function,
},
});
</script>
That would let only the parent have the ref to your template's wrapper.
If you're seeing the wrapper.value as null then make sure the element you're trying to get the ref to isn't hidden under a false v-if. Vue will not instantiate the ref until the element is actually required.
I realize this answer is not for the current question, but it is a top result for "template ref null vue 3 composition api" so I suspect more like me will come here and will appreciate this diagnosis.

Passing props to Vue root instance via attributes on element the app is mounted on

I am terribly new to Vue, so forgive me if my terminology is off. I have a .NET Core MVC project with small, separate vue pages. On my current page, I return a view from the controller that just has:
#model long;
<div id="faq-category" v-bind:faqCategoryId="#Model"></div>
#section Scripts {
<script src="~/scripts/js/faqCategory.js"></script>
}
Where I send in the id of the item this page will go grab and create the edit form for. faqCategory.js is the compiled vue app. I need to pass in the long parameter to the vue app on initialization, so it can go fetch the full object. I mount it with a main.ts like:
import { createApp } from 'vue'
import FaqCategoryPage from './FaqCategoryPage.vue'
createApp(FaqCategoryPage)
.mount('#faq-category');
How can I get my faqCategoryId into my vue app to kick off the initialization and load the object? My v-bind attempt seems to not work - I have a #Prop(Number) readonly faqCategoryId: number = 0; on the vue component, but it is always 0.
My FaqCategoryPAge.vue script is simply:
<script lang="ts">
import { Options, Vue } from "vue-class-component";
import { Prop } from 'vue-property-decorator'
import Card from "#/Card.vue";
import axios from "axios";
import FaqCategory from "../shared/FaqCategory";
#Options({
components: {
Card,
},
})
export default class FaqCategoryPage extends Vue {
#Prop(Number) readonly faqCategoryId: number = 0;
mounted() {
console.log(this.faqCategoryId);
}
}
</script>
It seems passing props to root instance vie attributes placed on element the app is mounting on is not supported
You can solve it using data- attributes easily
Vue 2
const mountEl = document.querySelector("#app");
new Vue({
propsData: { ...mountEl.dataset },
props: ["message"]
}).$mount("#app");
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app" data-message="Hello from HTML">
{{ message }}
</div>
Vue 3
const mountEl = document.querySelector("#app");
Vue.createApp({
props: ["message"]
}, { ...mountEl.dataset }).mount("#app");
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/3.0.0/vue.global.js"></script>
<div id="app" data-message="Hello from HTML">
{{ message }}
</div>
Biggest disadvantage of this is that everything taken from data- attributes is a string so if your component expects something else (Number, Boolean etc) you need to make conversion yourself.
One more option of course is pushing your component one level down. As long as you use v-bind (:counter), proper JS type is passed into the component:
Vue.createApp({
components: {
MyComponent: {
props: {
message: String,
counter: Number
},
template: '<div> {{ message }} (counter: {{ counter }}) </div>'
}
},
}).mount("#app");
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/3.0.0/vue.global.js"></script>
<div id="app">
<my-component :message="'Hello from HTML'" :counter="10" />
</div>
Just an idea (not a real problem)
Not really sure but it can be a problem with Props casing
HTML attribute names are case-insensitive, so browsers will interpret any uppercase characters as lowercase. That means when you're using in-DOM templates, camelCased prop names need to use their kebab-cased (hyphen-delimited) equivalents
Try to change your MVC view into this:
<div id="faq-category" v-bind:faq-category-id="#Model"></div>
Further to Michal Levý's answer regarding Vue 3, you can also implement that pattern with a Single File Component:
app.html
<div id="app" data-message="My Message"/>
app.js
import { createApp } from 'vue';
import MyComponent from './my-component.vue';
const mountEl = document.querySelector("#app");
Vue.createApp(MyComponent, { ...mountEl.dataset }).mount("#app");
my-component.vue
<template>
{{ message }}
</template>
<script>
export default {
props: {
message: String
}
};
</script>
Or you could even grab data from anywhere on the parent HTML page, eg:
app.html
<h1>My Message</h1>
<div id="app"/>
app.js
import { createApp } from 'vue';
import MyComponent from './my-component.vue';
const message = document.querySelector('h1').innerText;
Vue.createApp(MyComponent, { message }).mount("#app");
my-component.vue
<template>
{{ message }}
</template>
<script>
export default {
props: {
message: String
}
};
</script>
To answer TheStoryCoder's question: you would need to use a data prop. My answers above demonstrate how to pass a value from the parent DOM to the Vue app when it is mounted. If you wanted to then change the value of message after it was mounted, you would need to do something like this (I've called the data prop myMessage for clarity, but you could also just use the same prop name message):
<template>
{{ myMessage }}
<button #click="myMessage = 'foo'">Foo me</button>
</template>
<script>
export default {
props: {
message: String
},
data() {
return {
myMessage: this.message
}
}
};
</script>
So I'm not at all familiar with .NET and what model does, but Vue will treat the DOM element as a placeholder only and it does not extend to it the same functionality as the components within the app have.
so v-bind is not going to work, even without the value being reactive, the option is not there to do it.
you could try a hack to access the value and assign to a data such as...
const app = Vue.createApp({
data(){
return {
faqCategoryId: null
}
},
mounted() {
const props = ["faqCategoryId"]
const el = this.$el.parentElement;
props.forEach((key) => {
const val = el.getAttribute(key);
if(val !== null) this[key] = (val);
})
}
})
app.mount('#app')
<script src="https://unpkg.com/vue#3.0.0-rc.11/dist/vue.global.prod.js"></script>
<div id="app" faqCategoryId="12">
<h1>Faq Category Id: {{faqCategoryId}}</h1>
</div>
where you get the value from the html dom element, and assign to a data. The reason I'm suggesting data instead of props is that props are setup to be write only, so you wouldn't be able to override them, so instead I've used a variable props to define the props to look for in the dom element.
Another option
is to use inject/provide
it's easier to just use js to provide the variable, but assuming you want to use this in an mvc framework, so that it is managed through the view only. In addition, you can make it simpler by picking the exact attributes you want to pass to the application, but this provides a better "framework" for reuse.
const mount = ($el) => {
const app = Vue.createApp({
inject: {
faqCategoryId: {
default: 'optional'
},
},
})
const el = document.querySelector($el)
Object.keys(app._component.inject).forEach(key => {
if (el.getAttribute(key) !== null) {
app.provide(key, el.getAttribute(key))
}
})
app.mount('#app')
}
mount('#app')
<script src="https://unpkg.com/vue#3.0.0-rc.11/dist/vue.global.prod.js"></script>
<div id="app" faqCategoryId="66">
<h1>Faq Category Id: {{faqCategoryId}}</h1>
</div>
As i tried in the following example
https://codepen.io/boussadjra/pen/vYGvXvq
you could do :
mounted() {
console.log(this.$el.parentElement.getAttribute("faqCategoryId"));
}
All other answers might be valid, but for Vue 3 the simple way is here:
import {createApp} from 'vue'
import rootComponent from './app.vue'
let rootProps = {};
createApp(rootComponent, rootProps)
.mount('#somewhere')

Vuejs vue-nav-tabs change title of tabs [duplicate]

Let's say I have a main Vue instance that has child components. Is there a way of calling a method belonging to one of these components from outside the Vue instance entirely?
Here is an example:
var vm = new Vue({
el: '#app',
components: {
'my-component': {
template: '#my-template',
data: function() {
return {
count: 1,
};
},
methods: {
increaseCount: function() {
this.count++;
}
}
},
}
});
$('#external-button').click(function()
{
vm['my-component'].increaseCount(); // This doesn't work
});
<script src="http://vuejs.org/js/vue.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="app">
<my-component></my-component>
<br>
<button id="external-button">External Button</button>
</div>
<template id="my-template">
<div style="border: 1px solid; padding: 5px;">
<p>A counter: {{ count }}</p>
<button #click="increaseCount">Internal Button</button>
</div>
</template>
So when I click the internal button, the increaseCount() method is bound to its click event so it gets called. There is no way to bind the event to the external button, whose click event I am listening for with jQuery, so I'll need some other way to call increaseCount.
EDIT
It seems this works:
vm.$children[0].increaseCount();
However, this is not a good solution because I am referencing the component by its index in the children array, and with many components this is unlikely to stay constant and the code is less readable.
In the end I opted for using Vue's ref directive. This allows a component to be referenced from the parent for direct access.
E.g.
Have a component registered on my parent instance:
var vm = new Vue({
el: '#app',
components: { 'my-component': myComponent }
});
Render the component in template/html with a reference:
<my-component ref="foo"></my-component>
Now, elsewhere I can access the component externally
<script>
vm.$refs.foo.doSomething(); //assuming my component has a doSomething() method
</script>
See this fiddle for an example: https://jsfiddle.net/0zefx8o6/
(old example using Vue 1: https://jsfiddle.net/6v7y6msr/)
Edit for Vue3 - Composition API
The child-component has to return the function in setup you want to use in the parent-component otherwise the function is not available to the parent.
Note: <sript setup> doc is not affacted, because it provides all the functions and variables to the template by default.
You can set ref for child components then in parent can call via $refs:
Add ref to child component:
<my-component ref="childref"></my-component>
Add click event to parent:
<button id="external-button" #click="$refs.childref.increaseCount()">External Button</button>
var vm = new Vue({
el: '#app',
components: {
'my-component': {
template: '#my-template',
data: function() {
return {
count: 1,
};
},
methods: {
increaseCount: function() {
this.count++;
}
}
},
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<my-component ref="childref"></my-component>
<button id="external-button" #click="$refs.childref.increaseCount()">External Button</button>
</div>
<template id="my-template">
<div style="border: 1px solid; padding: 2px;" ref="childref">
<p>A counter: {{ count }}</p>
<button #click="increaseCount">Internal Button</button>
</div>
</template>
For Vue2 this applies:
var bus = new Vue()
// in component A's method
bus.$emit('id-selected', 1)
// in component B's created hook
bus.$on('id-selected', function (id) {
// ...
})
See here for the Vue docs.
And here is more detail on how to set up this event bus exactly.
If you'd like more info on when to use properties, events and/ or centralized state management see this article.
See below comment of Thomas regarding Vue 3.
You can use Vue event system
vm.$broadcast('event-name', args)
and
vm.$on('event-name', function())
Here is the fiddle:
http://jsfiddle.net/hfalucas/wc1gg5v4/59/
A slightly different (simpler) version of the accepted answer:
Have a component registered on the parent instance:
export default {
components: { 'my-component': myComponent }
}
Render the component in template/html with a reference:
<my-component ref="foo"></my-component>
Access the component method:
<script>
this.$refs.foo.doSomething();
</script>
Say you have a child_method() in the child component:
export default {
methods: {
child_method () {
console.log('I got clicked')
}
}
}
Now you want to execute the child_method from parent component:
<template>
<div>
<button #click="exec">Execute child component</button>
<child-cmp ref="child"></child_cmp> <!-- note the ref="child" here -->
</div>
</template>
export default {
methods: {
exec () { //accessing the child component instance through $refs
this.$refs.child.child_method() //execute the method belongs to the child component
}
}
}
If you want to execute a parent component method from child component:
this.$parent.name_of_method()
NOTE: It is not recommended to access the child and parent component like this.
Instead as best practice use Props & Events for parent-child communication.
If you want communication between components surely use vuex or event bus
Please read this very helpful article
This is a simple way to access a component's methods from other component
// This is external shared (reusable) component, so you can call its methods from other components
export default {
name: 'SharedBase',
methods: {
fetchLocalData: function(module, page){
// .....fetches some data
return { jsonData }
}
}
}
// This is your component where you can call SharedBased component's method(s)
import SharedBase from '[your path to component]';
var sections = [];
export default {
name: 'History',
created: function(){
this.sections = SharedBase.methods['fetchLocalData']('intro', 'history');
}
}
Using Vue 3:
const app = createApp({})
// register an options object
app.component('my-component', {
/* ... */
})
....
// retrieve a registered component
const MyComponent = app.component('my-component')
MyComponent.methods.greet();
https://v3.vuejs.org/api/application-api.html#component
Here is a simple one
this.$children[indexOfComponent].childsMethodName();
I am not sure is it the right way but this one works for me.
First import the component which contains the method you want to call in your component
import myComponent from './MyComponent'
and then call any method of MyCompenent
myComponent.methods.doSomething()
Declare your function in a component like this:
export default {
mounted () {
this.$root.$on('component1', () => {
// do your logic here :D
});
}
};
and call it from any page like this:
this.$root.$emit("component1");
If you're using Vue 3 with <script setup> sugar, note that internal bindings of a component are closed (not visible from outside the component) and you must use defineExpose(see docs) to make them visible from outside. Something like this:
<script setup lang="ts">
const method1 = () => { ... };
const method2 = () => { ... };
defineExpose({
method1,
method2,
});
</script>
Since
Components using are closed by default
Sometimes you want to keep these things contained within your component. Depending on DOM state (the elements you're listening on must exist in DOM when your Vue component is instantiated), you can listen to events on elements outside of your component from within your Vue component. Let's say there is an element outside of your component, and when the user clicks it, you want your component to respond.
In html you have:
Launch the component
...
<my-component></my-component>
In your Vue component:
methods() {
doSomething() {
// do something
}
},
created() {
document.getElementById('outsideLink').addEventListener('click', evt =>
{
this.doSomething();
});
}
I have used a very simple solution. I have included a HTML element, that calls the method, in my Vue Component that I select, using Vanilla JS, and I trigger click!
In the Vue Component, I have included something like the following:
<span data-id="btnReload" #click="fetchTaskList()"><i class="fa fa-refresh"></i></span>
That I use using Vanilla JS:
const btnReload = document.querySelector('[data-id="btnReload"]');
btnReload.click();