vue3 devtools: How to access component methods using Options API? - vue.js

In devtools with Vue2 I can access my components methods by selecting a component in vue devtools and then type $vm0.myMethod() in the console.
export default {
// ...
methods: {
myMethod() {
console.log('hello');
},
}
// ...
}
Now I'm using Vue3 with options API. How can I still access my component methods?

Given that the methods are specified in methods for options API: {
<script>
export default {
expose: ['publicMethod'],
methods: {
privateMethod() {...}
publicMethod() {...}
},
}
</script>
For composition API, it's presumed that component methods are returned from setup function:
<script>
export default {
setup(props, ctx) {
function justLocalFunction() {...}
function privateMethod() {...}
function publicMethod() {...}
ctx.expose({ publicMethod })
return { privateMethod, publicMethod };
}
}
</script>
This is implicitly done with script setup syntax:
<script setup>
export default {
setup(props, ctx) {
{
// Block scope isn't magically transformed
function justLocalFunction() {...}
}
function privateMethod() {...}
function publicMethod() {...}
defineExpose({ publicMethod })
}
}
</script>
Here only publicMethod is available on component ref. While privateMethod and publicMethod are exposed on internal component instance, which can be accessed as getCurrentInstance().proxy.privateMethod, etc inside setup block, and as devtools $vm.proxy via Vue devtools.
If there's a chance that justLocalFunction needs to be accessed later, returning it from setup function will make it easier for testing and debugging.

Related

this.$forceUpdate equivalent in Vue 3 - Composition API?

In Vue 2, instance method this.$forceUpdate() could be used to update the component manually. How can we force update component in Vue 3 - Composition API (inside setup method) ?
setup(props, context) {
const doSomething = () => {
/* how to call $forceUpdate here? */
}
return {
doSomething
}
}
Thanks, in advance.
If using Options API:
<script lang="ts">
import {getCurrentInstance, defineComponent} from 'vue'
export default defineComponent({
setup() {
const instance = getCurrentInstance();
instance?.proxy?.$forceUpdate();
}
})
</script>
If using Composition API with <script setup>
<script setup lang="ts">
import { getCurrentInstance } from 'vue'
const instance = getCurrentInstance();
instance?.proxy?.$forceUpdate();
</script>
When I need to force an update in vue I usually add a key with a value I can change, which will then force vue to update it. That should work in vue 3 as well, though I admit I haven't ever tried it. Here's an example:
<template>
<ComponentToUpdate
:key="updateKey"
/>
</template>
<script>
export default {
data() {
return {
updateKey: 0,
};
},
methods: {
forceUpdate() {
this.updateKey += 1;
}
}
}
</script>
You can read more about it here: https://michaelnthiessen.com/key-changing-technique/
$forceUpdate is still available in Vue3, but you won't have access to it in the setup() function. If you absolutely need to use it, you can use object API component or this fancy trick...
app.component("my-component", {
template: `...`,
methods: {
forceUpdate(){
this.$forceUpdate()
}
},
setup(props) {
const instance = Vue.getCurrentInstance();
Vue.onBeforeMount(()=>{
// instance.ctx is not populated immediately
instance.ctx.forceUpdate();
})
return {doSomething};
},
})
If this seems like a ridiculous solution, trust your Judgement. Ideally your application would not rely on forceUpdate. If you are relying on it, it likely means that something is miss-configured, and that should be the first thing to resolve.

Call Method from Differnt Vue Component (file)

I need to call method from different vue component which resides in different file I've tried with ref and didn't work for me and I know this might be simple but im new to vue please help me to do this task.
Both files reside in same folder
DefinitionManager - need to call method inside this component
methods: {
closeDrawer() {
this.drawer = false;
}
DefinitionMaker - I need to call above method by this component method
methods: {
CallDrawerMethod() {
Call Method from Definition Manager component
}
just add a ref in the child component and call that.
<template>
<div>
<DefinitionManager ref="definationManager"/>
</div>
<t/emplate>
<script>
import DefinitionManager from './DefinitionManager.vue'
export default {
components: {
DefinitionManager
},
methods: {
CallDrawerMethod() {
this.$refs.definationManager.closeDrawer();
}
}
}
</script>

vue 3 directives listen vue emit

What i want to achieve is to build a loading vue directives,
function with v-loading directive will render spinner and block event till function promise resolve or reject.
What i had tried so far:
use addEventListener, but it can only listen dom's native event, not vue event
hijack vue $emit function, but get a warning said that don't override vue native function named $, even if this solution work, i think this is a bad solution.
in directives argument, binding.instance[binding.value.name] refer to onEvent function in component, i tried override it but it doesn't work. When onEvent trigger again, it run old onEvent before override.
third party event emitter(eg, mitt). this method works well, but custom-component have to write extra code to emit event.
As example code below,
user of v-loading have to remember to write 2 emit (mitt and vue's emit).
It is not that straight forward, and it has extra dependency.
// mitt solution
// custom-component template
<custom-component v-loading:event="onEvent">
// custom-component script
setup(props, {emit}) {
function emitEvent() {
emit("event");
// bad: have to remember to write this extra line, and it is third party dependency
mittEmitter.emit("event");
}
}
So, any other solution to listen vue's event(not dom's native event) from vue's $emit?
LoadingDirective.ts
import { Directive } from "vue";
const Loading: Directive = {
mounted(el, binding) {
const eventName = binding.arg;
const onEvent = binding.value;
// I want to listen vue's event(eventName) here
// do something extra
onEvent(); // original onEvent() function to run in App.vue
// do something extra
}
};
export default Loading;
App.vue
<template>
<!-- when onEvent triggered, a spinner will be render in custom-component -->
<custom-component v-loading:event="onEvent" />
</template>
CustomComponent.vue
<script lang="ts">
import { defineComponent } from "vue";
export default defineComponent({
setup(props, {emit}) {
function emitEvent() {
// use only vue's emit
emit("event")
}
return {
onEvent
};
}
});
</script>
The Vue 3 documentation recommends using an external library such as mitt or tiny-emitter.
JSFiddle Example
<template>
<div id="app">
<custom-component v-loading="eventHandler" />
</div>
</template>
const emitter = mitt();
const customComponent = { template: '<h1>Example</h1>' };
const app = Vue.createApp({
components: { customComponent },
setup() {
setTimeout(() => {
emitter.emit('loadingEvent', { colour: 'Green' });
}, 1000);
const eventHandler = e => console.log('Handled!', e);
return { eventHandler };
},
});
app.directive('loading', {
mounted(el, binding) {
const func = binding.value;
emitter.on('loadingEvent', data => {
// your logic here...
func(data);
});
},
});
app.mount('#app');

How to access 'layout' or 'page' function directly in component?

How can I access a layout- or page-function directly in a component? Is there a special variable like $root or $parent?
I found a way to do this, but it seems dirty. I saw the component structure using Vue DevTool, and I found the layout is the root's child, so I called the layout's function like this:
this.$root.$children[2].getMap()
Is there a cleaner way?
You could use Vue's provide/inject feature for this. For instance, a page/layout could provide the getMap():
<template>
<MapButton />
</template>
<script>
export default {
name: 'MapPage',
provide() {
return {
getMap() {
return { /* map */ }
}
}
}
}
</script>
...and then any child component on that page/layout could inject the method where needed:
<template>
<button #click="getMap">Get map</button>
</template>
<script>
export default {
name: 'MapButton',
inject: {
getMap: {
default() {
return () => {
console.log('no map')
}
}
}
},
mounted() {
console.log('map', this.getMap())
}
}
</script>

Using methods inside of a method in Vue JS

I'm trying to access a method within another method in Vue. I'm positive I saw this done in a tutorial I followed.
Here is my code
<template>
<button #click="submit">Submit</button>
</template>
<script>
export default {
name: 'FormExample',
methods: {
submit() {
validate();
alert('done');
},
validate() {
alert('validated')
}
},
}
</script>
When I trigger the submit() it says the validate is not defined.
I'm doing as i'd like to use the validate method in multiple methods without repeating code. Where should the validation code live if not as a method?
You need to use this for calling method.
methods: {
submit() {
this.validate();
alert('done');
},
validate() {
alert('validated')
}
},