Vue Composition Api - call child component's method which uses render function - javascript

I'm using Vue composition-api with Vue2.
I ran into a problem when I tried to call a method of a component with a render function from its parent.
Without render function, it's ok.
TemplateComponent.vue
<template>
...
</template>
<script lang="ts">
import { defineComponent } from '#vue/composition-api'
export default defineComponent({
setup (props, context) {
const doSomething = () => {
console.log('doSomething')
}
return {
// publish doSomething method.
doSomething
}
}
})
</script>
So, parent component can call TemplateComponent's method like this.
TopPage.vue
<template>
<TemplateComponent ref="componentRef" />
</template>
<script lang="ts">
import { defineComponent, ref, onMounted } from '#vue/composition-api'
import TemplateComponent from '#/components/TemplateComponent.vue'
export default defineComponent({
components: { TemplateComponent },
setup (props, context) {
const componentRef = ref()
onMounted(() => {
componentRef.value.doSomething()
})
}
})
</script>
With render function, I can't find way to call method.
RenderComponent.vue
<script lang="ts">
import { defineComponent, h } from '#vue/composition-api'
export default defineComponent({
components: { TemplateComponent },
setup (props, context) {
const doSomething = () => {
console.log('doSomething')
}
// setup method should return render function.
return () => h('div', 'Hello world!!')
}
})
</script>
When declare render function with composition api, we should return render function in setup method.
https://v3.vuejs.org/guide/composition-api-setup.html#usage-with-render-functions
In this case, I don't understand how to publish doSomething method.
Is there a way to solve this problem?

expose context method exists to combine render function and public instance methods in setup:
context.expose({ doSomething })
return () => ...

Related

Vue 3 - Options API vs Composition API - Load external file into component

I have the following file which is an external file with functions (language.js). I also have created a other component which needs to use language.js (it needs to use the languageText funcion inside language.js). I did used it in a Composition API component. But now I want to get it working in a Options API component. Please check the function inside methods called languageSelector. Inside this function I want to use the global function from language.js (languageText())
Any help?
Options API template (Form.vue)
<script>
import languageText from '#/composables/language';
export default defineComponent({
name: 'Form',
props: {
processingData: Object,
formData: Object
},
emits: ["gateway"],
components: {
Icon
},
data() {
return {
fieldData: this.formData,
}
},
methods: {
languageSelector(data) {
const h = languageText(data) **I want to USE the FUNCTION here.**
console.log(h)
return languageText(data)
},
}
language.js
import { ref, computed, watch } from 'vue';
import { useI18n } from "vue-i18n";
import { useStore } from "vuex";
export default function language() {
const store = useStore();
const i18n = useI18n();
const language = computed(() => {
return store.getters.currentUser.language;
});
function languageText(json) {
const obj = JSON.parse(json)
return obj[language.value]
}
return {
languageText
}
}

Vue 3: How to Access Setup Variable in Component Function

Consider the following simple example using the composition API in Vue 3. I'm trying to have an instance of test available in the functions of my component.
<script>
import { defineComponent, ref, onMounted } from 'vue'
export default defineComponent({
name: 'Test',
setup(){
let test = ref()
onMounted(() => {
doSomething()
})
return{
test,
doSomething
}
}
})
function doSomething(){
console.log(test) //<-- undefined
console.log(this.test) //<-- undefined
}
</script>
How do I access test inside doSomething()? My understanding is that anything returned by setup() should be available throughout the component much like a data() attributes from the options API.
you have to pass the ref as a parameter
<script>
import { defineComponent, ref, onMounted } from 'vue'
export default defineComponent({
name: 'Test',
setup () {
let test = ref(null)
onMounted(() => {
doSomething(test.value)
})
return {
test,
doSomething
}
}
})
function doSomething (param) {
console.log(param); // null
}
</script>
another approach:
// functions.js
import { ref } from 'vue'
export let test = ref(null)
// vue-file
<script>
import { defineComponent, ref, onMounted } from 'vue'
import { test } from '../utils/functions.js'
export default defineComponent({
name: 'Test',
setup () {
onMounted(() => {
doSomething(test)
})
return {
test,
doSomething
}
}
})
function doSomething (param) {
console.log(test.value); // <-- instant access
console.log(param.value); // <-- import via parameter
}
</script>

How can I set a ref to the value of an ajax call without triggering #update:modelValue in vue 3?

I have a checkbox that when clicked triggers an ajax call using the #update:modelValue syntax in the template. However whenever this page loads the ajax call gets called.
This is happening because when the setup() function runs I set the isPushNotificationChecked ref and then I update it in the onMounted function to be the response of a different ajax call.
Here is the code:
<template>
<ion-checkbox
slot="start"
v-model="isPushNotificationChecked"
#update:modelValue="updatePushNotifications"
></ion-checkbox>
</template>
<script>
import {
IonCheckbox,
} from "#ionic/vue";
import { defineComponent, ref, onMounted } from "vue";
import axios from "axios";
import useToast from "#/services/toast";
export default defineComponent({
name: "Settings",
components: {
IonCheckbox,
},
setup() {
const isPushNotificationChecked = ref(false);
onMounted(async () => {
const response = await axios.get("settings");
// Since I change the value here #update:modelValue in template triggers updatePushNotifications
isPushNotificationChecked.value = response.data.notifications_enabled;
});
// This gets triggered on page load when it shouldn't
const updatePushNotifications = async () => {
if (isPushNotificationChecked.value) {
axios.post("notifications/enable");
} else {
axios.post("notifications/disable");
}
useToast().success("Push notifications updated");
};
return {
isPushNotificationChecked,
updatePushNotifications,
};
},
});
</script>
How can I go about setting the ref value to be the response of an ajax call without removing the behaviour of clicking the checkbox and triggering the ajax call?
My solution was to instead use a watcher that is initialised in the onMounted() function and remove the #update:modelValue="updatePushNotifications":
<template>
<ion-checkbox
slot="start"
v-model="isPushNotificationChecked"
></ion-checkbox>
</template>
<script>
import {
IonCheckbox,
} from "#ionic/vue";
import { defineComponent, ref, onMounted } from "vue";
import axios from "axios";
import useToast from "#/services/toast";
export default defineComponent({
name: "Settings",
components: {
IonCheckbox,
},
setup() {
const isPushNotificationChecked = ref(false);
onMounted(async () => {
const response = await axios.get("settings");
isPushNotificationChecked.value = response.data.notifications_enabled;
// Watcher setup after ajax call.
watch(isPushNotificationChecked, (value) => {
if (value) {
axios.post("notifications/enable");
} else {
axios.post("notifications/disable");
}
useToast().success("Push notifications updated");
});
});
return {
isPushNotificationChecked
};
},
});
</script>

Call a method from another Component after the Async function reactjs

I have 2 components, the first component has a function that calls after the async function of the second component, what I want to do is something like vue's this.$emit() function that calls a listener from that component anytime, how can I do that in react?
This is my first component
import React, { Component } from 'react';
import SecondComponent from '../Path/to/second/component'
class MainMenu extends Component {
callThis (data) {
console.log(data)
}
render () {
return <SecondComponent onDataReceived = {this.callThis} />
}
}
export default FirstComponent
And this is my SecondComponent
import React, { Component } from 'react';
class SecondComponent extends Component {
async asyncFunction () {
const data = await getDataFromApi()
// call the function from first component here...
}
render () {
return <button onClick={() => this.asyncFuncion} />
}
}
export default FirstComponent
Your second component must invoke asyncFuncion, and then inside asyncFuncion you can call the callThis function from the props
class SecondComponent extends Component {
async asyncFunction () {
const data = await getDataFromApi()
this.props.onDataReceived(data)
}
render () {
return <button onClick={() => this.asyncFuncion()} />
}
}
and do not forget to bind that callThis as well, or just use fat arrow function:
class MainMenu extends Component {
callThis = (data) => {
console.log(data)
}
On your first component, you are sending a props to your second components.
Here is the documentation : https://reactjs.org/docs/components-and-props.html
To access onDataReceived in your second component you could write :
async asyncFunction () {
const data = await getDataFromApi()
this.props.onDataReceived(data);
}
this is how you can receive data/use methods from parent passed props:
async asyncFunction () {
const data = await getDataFromApi()
// call the function from first component here...
this.props.onDataReceived(data);
}

vuex and axios debugging

I'm going crazy, I have a working api that sends data, I connected it to a VueJS app and it was working fine. I'm trying to implement Vuex and I'm stuck. Here's my store.js file
import Vue from 'vue';
import Vuex from 'vuex';
import axios from 'axios'
Vue.use(Vuex);
const state = {
message: "I am groot",
articles: []
}
const getters = {
getArticles: (state) => {
return state.articles;
}
}
const actions = {
getArticles: ({ commit }, data) => {
axios.get('/articles').then( (articles) => {
commit('GET_ARTICLES', articles);
console.log(articles); // Trying to debug
}, (err) => {
console.log(err);
})
}
}
const mutations = {
GET_ARTICLES: (state, {list}) => {
state.articles = list;
}
}
const store = new Vuex.Store({
state,
getters,
mutations,
actions,
mutations
});
console.log(store.state.articles); // this lines works but data is empty
export default store
The console.log within axios call doesn't run and store.state.articles is empty. I must be missing something. I'm just trying to console the articles data on page load...
Please help, I'm near insanity :)
Component :
<template>
<div class="container">
<h1>Test component yo !</h1>
<p>{{ message }}</p>
</div>
</template>
<script>
export default {
name: 'Test',
computed: {
message() {
return this.$store.state.message
}
},
mounted: () => {
this.$store.dispatch('getArticles')
}
}
</script>
App.js :
import Vue from 'vue';
import ArticlesViewer from './articles_viewer.vue';
import UserArticles from './user_articles.vue';
import App from './app.vue'
import store from './store'
new Vue({
el: '#app-container',
store,
render: h => h(App)
})
You define the mounted lifecycle hook of your component using an arrow function.
As per the documentation:
Don’t use arrow functions on an instance property or callback (e.g. vm.$watch('a', newVal => this.myMethod())). As arrow functions are bound to the parent context, this will not be the Vue instance as you’d expect and this.myMethod will be undefined.
You should define it like so:
mounted: function () {
this.$store.dispatch('getArticles');
}
Or, use the ECMAScript 5 shorthand:
mounted() {
this.$store.dispatch('getArticles');
}
Now, your dispatch method will be called correctly, populating your articles array.

Categories

Resources