How to pass props from parent components to child components - vue.js

I have three components namely Home.vue, Searchform.vue and Searchresults.vue respectively in my project.
Home.vue - is the view that i'm showing the other two components
Searchform.vue - is the component that hold the search input fields
Searchresults.vue - is the components that shows the result of search input in a "table form"
So when a user has make a search query and a result is render in the table. i make a method to get a row clicked into a data and pass an a props to Searchform.vue then bind the props on that on the home.vue. But the props data is not displaying on the Searchform.vue components resulting no showing on home.vue view.
Below is the code of two components and the home.vue
Home.vue
<template>
<div class="home">
<div class="example" v-if="isLoading === true">
<a-spin size="large" />
</div>
<Navbar />
<div class="container">
<SearchForm
v-on:search="search"
:selectedinterest="selectedinterest"
/>
<SearchResults
v-if="interests.length > 0"
v-bind:interests="interests"
v-bind:reformattedSearchString="reformattedSearchString"
/>
<ErrorMessage
v-if="interests.length < 0"
v-bind:interests="interests"
/>
<Footer />
</div>
</div>
</template>
Searchresults.vue
export default {
data() {
return {
selectedinterest: []
}
}
addSelection(interest) {
this.selectedinterest.push(interest.name))
}
}
And lastly the Searchform.vue that i want to pass the props to and bind it on the home.vue to get the data
export default {
name: 'SearchForm',
props: [
'selectedinterest'
]
}
Please how can i pass the props 'selectedinterest' to the home.vue and searchform.vue from the searchresults.vue components.

Props in - events out
Searchresults.vue
export default {
data() {
return {
selectedinterest: []
}
}
addSelection(interest) {
this.selectedinterest.push(interest.name));
this.$emit('onInterestSelected', this.selectedinterest);
}
}
Home.vue
...
<SearchResults
v-if="interests.length > 0"
v-bind:interests="interests"
v-bind:reformattedSearchString="reformattedSearchString"
v-on:onInterestSelected="updateSelectedInterest"
/>
<!-- don't forget create method updateSelectedInterest(updatedInterest) -->
...

One way to go could be to emit an event in your Searchresults.vue component inside the addSelection method.
addSelection(interest) {
this.selectedinterest.push(interest.name))
this.$emit('addedSelection', this.selectedinterest);
}
In your parent component you will listen to your event and use a method to store the event data and then pass it on as props to other components.
<SearchResults
v-if="interests.length > 0"
v-bind:interests="interests"
v-bind:reformattedSearchString="reformattedSearchString"
#addedSelection="addedSelectionTriggered"
/>
Using the addedSelectionTriggered method in your parent component you can store the emitted selectedinterest array and pass it on as props.

Related

Vue: Reuse loading spinner template and logic

I've multiple (20+) pages where I need the following code:
<template>
<template v-if="isLoading">
<Spinner full size="medium" />
</template>
<template v-else>
<p>Page data</p>
</template>
</template>
<script>
export default {
computed: {
isLoading() {
return this.$store.getters['loader/isLoading'];
},
},
};
</script>
I don't want to rewrite this part everytime I need it so is there a way to create something like a higher order component with access to the computed method and a way to add the hoc to the script tag of the different vue files? Or another way to archive this?
I could recommend extending the spinner component where you want to show the spinner. I've created a boilerplate setup that show a very simple implementation of this approach here.
The main idea is to expose a default slot for you spinner component, and wrap the page component in that slot.
<template>
<div>
<Spinner v-if="isLoading" full size="medium" />
<!-- Slot for component data -->
<slot v-else></slot>
</div>
</template>
<script>
export default {
computed: {
isLoading() {
return this.$store.getters['loader/isLoading'];
},
},
};
</script>
Then in any component that you want to show the spinner:
<template>
<spinner>
<!-- Pass here the component content to be shown after the spinner is hidden -->
</spinner>
</template>
<script>
import Spinner from "./spinner";
export default {
name: "Page1",
extends: Spinner,
components: { Spinner }
};
</script>

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(() => {