<script setup lang="ts">
function callSomething() {
something(); //not working
}
onMounted(() => {
function something() {
console.log("Hello, World");
}
});
</script>
<template>
<div>
<button #click="callSomething">Click</button>
</div>
</template>
In Vuejs I want to call a function from <script setup lang="ts"> which is defined in onMounted lifecycle hook. Though, I can call function/method from onMounted that defined in <script setup lang="ts">
Error in console:
Uncaught TypeError: something is not a function
As per it's name onMounted(), This life cycle hook always execute when component mounted for the first time. For a button click, you can call a method directly outside of this onMounted().
<button #click="callSomething">Click</button>
function callSomething() {
// Logic can come directly here.
}
The something function is defined only in the scope of the onMounted callback, try to define it outside it to be available for the hook and the other function :
<script setup lang="ts">
function callSomething() {
something(); //not working
}
function something() {
console.log("Hello, World");
}
onMounted(() => {
});
</script>
Related
How do you use the next tick within the setup script in vue3?
<script setup>
const msg = 'Hello!'
this.$nextTick(() => {
console.log("next tick ... do something")
});
</script>
<template>
<p>{{ msg }}</p>
</template>
I've used multiple different methods but I can't find one that works outside of the normal script tags.
Another method I tried was.
import Vue from "vue";
Vue.nextTick(() => {});
This is how you can use nextTick() in Vue 3.
<script setup>
import { ref, nextTick } from 'vue'
const count = ref(0)
async function increment() {
count.value++
// DOM not yet updated
console.log(document.getElementById('counter').textContent) // 0
await nextTick()
// DOM is now updated
console.log(document.getElementById('counter').textContent) // 1
}
</script>
<template>
<button id="counter" #click="increment">{{ count }}</button>
</template>
Here you can find more informations: https://vuejs.org/api/general.html#nexttick
<template>
<RecyclablesPopup ref="LVP" class="inline-block m-5px"></RecyclablesPopup>
</template>
<script setup>
import RecyclablesPopup from "../components/popups/RecyclablesPopup";
import { ref } from 'vue';
const LVP = ref(null);
// ... after mounted I have an event with a legacy component and onclick handler:
eventClick: function(calEvent)
{
console.log(LVP.value);
LVP.value.click();
}
</script>
At the end I get Uncaught TypeError: LVP.value.click is not a function after I clicked.
console.log returns me the proxy object as expected Proxy { <target>: Proxy, <handler>: {…} }
Why can't I call click()?
the click function should be exposed by the child component in order be accessed by the parent component :
RecyclablesPopup component
<script setup>
function click(){
//.......
}
defineExpose({
click
})
</script>
for more details please check https://vuejs.org/guide/essentials/template-refs.html#ref-on-component
If you are using script setup you can't access functions, variables, etc., defined inside the referenced component. To change that you have to use the defineExpose compiler macro inside RecyclablesPopup component - check more in documentation
//inside RecyclablesPopup
<script setup>
import { ref } from 'vue'
const click = () => {
//do something
}
defineExpose({
click,
})
</script>
You need to execute the function on the onMounted lifecycle to guarantee that the component is mounted to the DOM, as the value before the component is mounted will be undefined.
onMounted(() => {
eventClick()
})
For more resources
https://vuejs.org/api/composition-api-lifecycle.html#onmounted
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.
I'd like to test specific methods in my Vue Single File Components using Jest. I'm using Vue 3 with the Composition API, and I would like to use the <script setup> approach but it appears to prevent this.
This works:
Component:
<script>
export default {
setup() {
const testMethod = function(input) {
return input + 1;
};
return { testMethod };
},
};
</script>
Test:
test('should be 2', () => {
expect(TestComponent.setup().testMethod(1)).toBe(2); // success
});
This doesn't work:
Component:
<script setup>
const testMethod = function(input) {
return input + 1;
};
</script>
Test:
test('should be 2', () => {
expect(TestComponent.setup().testMethod(1)).toBe(2); // TypeError: Cannot destructure property 'expose' of 'undefined' as it is undefined.
expect(TestComponent.testMethod(1)).toBe(2); // TypeError: _testComponent.default.testMethod is not a function
});
Is there another way to accomplish this, or is accessing the methods within the component not possible with the <script setup> approach?
Edit: Specifically looking for solutions that don't require mounting the gadget with something like vue-test-utils.
The bindings declared in <script setup> are hidden by default, but you can expose any of them to the component instance with defineExpose():
// MyComponent.vue
<script setup>
const testMethod = function(input) {
return input + 1;
};
defineExpose({
testMethod
})
</script>
Then in your test, access the exposed bindings via wrapper.vm (the component instance from the #vue/test-utils wrapper):
// MyComponent.spec.js
import MyComponent from '#/components/MyComponent.vue'
test('should be 2', () => {
const wrapper = shallowMount(MyComponent)
expect(wrapper.vm.testMethod(1)).toBe(2)
})
demo
if you use the wrapper of the mount method from vue test #vue/test-utils. Then just call the methods using (wrapper.vm as any) it works. Only the typescript compiler is unable to recognize and raise the error as property does not exist but when you try using something like below it works.
Eg: (wrapper.vm as any).someMethod()
I am totally new to Vuejs and my question is:
Is there anyway for v-on to listen on click event, then execute a function which is defined in a module?
For example:
<button v-on:click="executeClick()"></button>
Will execute executeClick() in below module, which will be imported to Vue instance through require:
module.exports = {
executeClick: function () {
// do something
}
}
I am trying to keep vue instance's methods not to be crowded with a bunch of functions.
No, within the model you need features that are in this your component
soluction:
click.js
module.exports = {
executeClick: function () {
}
}
component.vue
<template>
<tag #click="$options.click.executeClick">
</template>
<script>
import click from 'click.js'
export default {
click: click
}
</script>