Vue component error: Expected Function, got String - vue.js

I'm using ionic with vue to develop an app. I made a custom button and want to include in another component. I'm trying to assign a function to the button but doesnt seem to work. The error i got is this:
[Vue warn]: Invalid prop: type check failed for prop "onClick". Expected Function, got String with value "addTodo".
First time using vue.Thanks in advance.
Button component
<template>
<ion-footer>
<ion-button fill="outline" expand="block" color="primary" #click="onClick">
<ion-ripple-effect></ion-ripple-effect>
{{text}}
</ion-button>
</ion-footer>
</template>
<script>
export default {
props: {
onClick:Function,
text:String,
}
}
</script>
Another Component
<template>
<ion-row>
<ion-col>
<main-btn text="Add Activity" onClick="addTodo"></main-btn>
</ion-col>
</ion-row>
</ion-grid>
</template>
<script>
import axios from 'axios';
export default {
data() {
return {
todos: []
}
},
methods: {
addTodo() {
this.$router.push({path: '/todos/add'})
}
},
created() {
axios.get('http://localhost:3001/todos')
.then(res => this.todos = res.data)
}
}
</script>

Just change it up, you don't need to pass the function as a prop.
In your button component:
#click="$emit('click')"
In your "Another Component"
<main-btn text="Add Activity" #click="addTodo"></main-btn>
Just bubble up the click event from the child component.

Your going backwards against the way Vue is intended to work. You always want to pass Props Down which consist of data the child components need and emit events upwards to the parents to handle the necessary function calls.
Your child component "main-btn" should look more like below
<ion-button fill="outline" expand="block" color="primary" #click="$emit("onClick")">
<ion-ripple-effect></ion-ripple-effect>
{{text}}
</ion-button>
This will allow any parent components to look for the onClick Event emitted by the child component and react accordingly. In your case the parent component only needs to change slightly.
<ion-col>
<main-btn text="Add Activity" #onClick="addTodo"></main-btn>
</ion-col>
The parent component is looking for an onClick event and reacts accordingly by calling its addTodo function
https://v2.vuejs.org/v2/guide/components.html#Listening-to-Child-Components-Events

Related

Calling function inside child component without an event?

Currently trying to use a method belonging to the parent
<p class="message-date text-center">
{{ $emit('format_date_day_month_year_time', message.date) }}
</p>
However I am getting the error.
Converting circular structure to JSON
--> starting at object with constructor 'Object'
How can I call a function inside a child component that does not rely on an event? I apologize for asking such a simple question but everything I was able to find on google is using $emit and using an event.
$emit was designed to only trigger an event on the current instance of vue. Therefore, it is not possible to receive data from another component this way.
For your case, I would suggest to use Mixins especially if you need to use certain functions among multiple vue components.
Alternately, let the child component call the the parent through $emit then receive the result from the parent through a prop.
Your code could be something as follows:
Child component
<template>
<p class="message-date text-center">
{{ date }}
</p>
</template>
<script>
export default {
name: 'Child',
props: {
date: String,
},
mounted() {
this.$emit("format-date", message.date);
},
}
</script>
Parent component
<template>
<Child :date="childDate" #format-date="formatChildDate" />
</template>
<script>
import Child from '#/components/Child';
export default {
components: {
Child,
},
data: () => ({
childDate: '',
}),
methods: {
formatChildDate(date) {
this.childDate = this.formatDateDayMonthYearTime(date)
},
formatDateDayMonthYearTime(date) {
//return the formatted date
},
},
}
</script>
with $emit you call a function where the Parent can listento.
where you are using it i would suggest a computed prop of the function.
But back to your Question here is a example of emiting and listen.
//Parent
<template>
<MyComponent #childFunction="doSomethingInParent($event)"/>
</template>
//Child
<template>
<button #click="emitStuff">
</template>
.....
methods:{
emitStuff(){
this.$emit(childFunction, somedata)
}
with the event you can give Data informations to a Parentcomponent.

How to use custom input component in vuejs?

While using Bootstrap-Vue as UI framework, I am trying to make a custom form component and use it in several parent components. Here is my form component
<template>
<b-form>
<b-input-group>
<b-form-input placeholder="Post Title"></b-form-input>
<wysiwyg-input placeholder="Post Content" />
</b-input-group>
</b-form>
</template>
and the parent component is
<FormFields :title="title" :content="content" />
How can i access the value in parent component from child component.
Note: I am using vue-quill editor as well.
Here is the codesandbox link:
https://codesandbox.io/s/mystifying-benz-w8wgu?file=/src/App.vue
Thanks in advance !
Define a prop on your child component and pass the data to it when you use it in the parent:
// ChildComponent.vue
export default {
props: {
someData: {}
}
}
// ParentComponent.vue
<template>
<child-component :some-data="myData"></child-component>
</template>
<script>
export default {
data() {
return {
myData: {...}
}
}
}
</script>

VueJS2: Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders

I'm creating very simple Popup Modal Using Vuejs2 and TailwindCss. However i encounter the error like below, when i'm tring to click on Button..
Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders.
In Parent Component
// CardModal
<template>
<div class="bg-white">
<div v-if="showing">
Modal
</div>
</div>
</template>
<script>
export default {
data() {
return {
showing: false,
}
}
}
</script>
Child Components
<button #click="showing = true" class="px-4 my-4 mx-3 bg-blue-400 py-1 font-bold rounded text-white">
Add Product
</button>
<!-- Modal -->
<cardModal :showing="showing" />
// Script
props: {
showing: {
// required: true,
type: Boolean,
}
},
Thanks in advance...
It's hard to understand your code but you can't change value of prop in your child component directly instead you can emit an event to your parent which change the value of prop for you.
e.g your child component which has
<template>
#click="$emit('show',true)"
</template>
//
props: {
showing: {
// required: true,
type: Boolean,
}
}
your parent
<cardModal :showing="showing" #show="showing=$event" />

Vue `vm.$on()` callback not working in parent when event is emitted from dynamic component child

I'm experiencing a problem where a custom event (swap-components) emitted from a dynamic component (A.vue or B.vue) is not being listened to correctly in the parent of the dynamic component (HelloWorld.vue).
Here is the source on GitHub (created using vue cli 3).
Here is a live demo showing the problem.
In the live demo, you'll see that clicking the button in the dynamic component with background color DOES NOT change the dynamic component. But when clicking the button below the background color (which originates in the HelloWorld.vue parent), the dynamic component DOES INDEED change.
Why is this happening and how to fix it?
Below I'll copy over the contents of the 3 main files of interest into this post:
HelloWorld.vue (the parent)
A.vue (sub component used in dynamic component)
B.vue (sub component used in dynamic component)
HelloWorld.vue:
<template>
<div>
<h1>The dynamic components ⤵️</h1>
<component
:is="current"
v-bind="dynamicProps"
></component>
<button
#click="click"
>Click me to swap components from within the parent of the dynamic component</button>
</div>
</template>
<script>
import A from "./A.vue";
import B from "./B.vue";
export default {
data() {
return {
current: "A"
};
},
computed: {
dynamicProps() {
return this.current === "A" ? { data: 11 } : { color: "black" };
}
},
methods: {
click() {
this.$emit("swap-components");
},
swapComponents() {
this.current = this.current === "A" ? "B" : "A";
}
},
mounted() {
this.$nextTick(() => {
// Code that will run only after the
// entire view has been rendered
this.$on("swap-components", this.swapComponents);
});
},
components: {
A,
B
},
props: {
msg: String
}
};
</script>
A.vue:
<template>
<section id="A">
<h1>Component A</h1>
<p>Data prop sent from parent: "{{ data }}"</p>
<button #click="click">Click me to swap components from within the dynamic component</button>
</section>
</template>
<script>
export default {
props: ["data"],
methods: {
click() {
this.$emit("swap-components");
}
}
};
</script>
B.vue:
<template>
<section id="B">
<h1>Component B</h1>
<p>Color prop sent from parent: "{{ color }}"</p>
<button #click="click">Click me to swap components from within the dynamic component</button>
</section>
</template>
<script>
export default {
props: ["color"],
methods: {
click() {
this.$emit("swap-components");
}
}
};
</script>
I'm guessing this is because the event listener is listening for a swap-components event emitted by the parent component itself. Perhaps you can fix that by listening for a swap-components event from the child component then emitting an event on the parent component.
<template>
<div>
<h1>The dynamic components ⤵️</h1>
<component
:is="current"
v-bind="dynamicProps"
#swap-components="$emit('swap-components')"
></component>
<button
#click="click"
>Click me to swap components from within the parent of the dynamic component</button>
</div>
</template>
Or you can call your method directly when the event is emitted by the child component ..
<template>
<div>
<h1>The dynamic components ⤵️</h1>
<component
:is="current"
v-bind="dynamicProps"
#swap-components="swapComponents"
></component>
<button
#click="click"
>Click me to swap components from within the parent of the dynamic component</button>
</div>
</template>
this is not bound to the context anymore when you use function. It is only limited to the function scope. Use arrow function to let this bound to the parent context.
Change:
this.$nextTick(function() {
With:
this.$nextTick(() => {

Component Declaration And Communication

I have added a component declaration to the default main.js file which is generated during the Webpack project creation process as
import Modal from '#/components/Modal'
Vue.component('modal', Modal)
And in the App.vue, I have
<modal v-show="showModal"></modal>
<button id="show-modal" v-on:click="showModal = true">Click to have a modal</button>
And they work fine. Now, I need to setup a "props down, events up" communication channel between the parent and a child. To do so, I need to add a property, called 'isActive', the Modal component so that the root component can send a message to the child component, that is
<modal isActive="showModal"></modal>
<button id="show-modal" v-on:click="showModal = true">Click to have a modal</button>
I guess the component declaration should be something like:
Vue.component('modal', {
props: ['isActive'],
Modal
})
It doesn't work, however, due to
Failed to mount component: template or render function not defined.
I have tried different variants without a luck.
My second question is that how a child event changes its parent data. For example, in the child component
<button class="modal-close is-large" v-on:click="closeModal"></button>
the closeModal event is handled in the following javacript code in the child component.
export default {
method: {
closeModal: function(event) {
...
}
}
}
How can I set its parent data showModal to false?
Update:
The code segment of Modal:
<template>
<div class="signin">
<div class="modal" v-bind:class="{ 'is-active': isActive }">
...
</div>
<button class="modal-close is-large" v-on:click="isActive = false"></button>
</div>
</div>
</template>
<script>
import axios from 'axios'
import _ from 'lodash'
import Notification from '#/components/Notification'
import { required, email } from 'vuelidate/lib/validators'
export default {
name: 'signin',
components: {
Notification
},
data: () => ({
isActive: true,
email: '',
...
}),
...
}
</script>
Bulma is used for styling. And the isActive is defined in the Modal. I think it needs to be changed to achieve "props down".
As it looks, your file /components/Modal contains a full definition of a component: the template, and the script parts for it. So you can just bind the component to the tag-name you want to use in your markup:
import Modal from '#/components/Modal'
Vue.component('modal', Modal)
This is basically what you had in the beginning. To pass properties to this component, add the props-line directly to your component, that is into /components/Modal:
...
export default {
name: 'signin',
components: {
Notification
},
props: ['isActive'],
data: () => ({
...
As for the second question, how to communicate back to the parent, have a look at Vue's Custom Events. Basically, your Modal component could issue a "close"-event like this:
methods: {
closeModal: function(event) {
this.$emit('modalClose')
}
}
and when you use the component, you could listen to it like this:
<modal v-bind:isActive="showModal" v-on:modalClose="showModal = false"></modal>
Note that you should use v-bind for providing the value to isActive. If you don't use v-bind, the value is just passed once when the component is created. This means, the component would never see a change to this prop when it is changed by the parent. By using v-bind, changes by the parent to this attribute are pushed down to the child-component, so the Modal actually sees the updated value and can react to it.