Executing child (component) function from the parent component - javascript

To avoid this becoming messy and the use of "native" jQuery/javascript I would like to call a function in the child component from the parent, the function I want to exeecute is change_map_data() from the child G_Map.vue in an elegant way, vueish way:
Parent.vue
<template>
<div class="col-md-12">
...
<i v-on:click="change_map_data">change markers</i>
...
<g-map v-bind:map_data="init_data.map"></g-map>
...
</div>
export default {
data() {
return {
init_data: {
map: {
map_ele: 'map'
}
}
}
}
}
</script>
</template>
G_Map.vue:
<template>
<div :id="map_data.map_ele" class="gmap"></div>
</template>
<script>
import init_map from '../../../assets/js/map.js';
export default {
props: ['map_data'],
methods: {
change_map_data: function() { // want to execute this from parent
alert();
}
}
}
</script>

If you want to call a method defined on a child component from the parent component, you'll have to get a reference to the child component and call its method directly:
<i v-on:click="$refs.map.change_map_data()">change markers</i>
<g-map v-bind:map_data="init_data.map" ref="map"></g-map>
If you're generating a dynamic number of maps, then you'll need to do something like this:
<div v-for="map, i of maps">
<i v-on:click="$refs.map[i].change_map_data()">change markers</i>
<g-map v-bind:map_data="map" ref="map"></g-map>
</div>

Related

Vue communcation between child to parent to another child

I got a <payment-child-component> which handles all the subscriptions and payments, i also have
another <check-active-child-component>
I want these two components to communicate. persay in the <payment-component> a user cancel's his subscription i want to fire a method i have in <check-active-component> which called checkActive()
So from payment-component emits to parent-component when the subscription cancel method is triggered and then fires the checkActive() method inside check-active-component
So if my logic is good, the exact question is: how do i fire a method from parent to child component?
To call a method of a child component from its parent, you can use ref. Here is an example:
Child Component:
export default {
name: "ChildComponent",
methods: {
childMethod(){
console.log("hello from child");
}
}
};
Parent Component:
<template>
<div id="app">
<ChildComponent ref="myChild"/>
</div>
</template>
<script>
import ChildComponent from "./components/ChildComponent";
export default {
name: "App",
components: {
ChildComponent
},
mounted(){
this.$refs.myChild.childMethod()
}
};
</script>
This one is what I am using on my app.
Parent.vue
<template>
<a #click="run">Click me to execute method from child</a>
</template>
<script>
export default {
name:"parent",
methods: {
run() {
this.$root.$refs.child.methodtoexecute();
}
}
}
</script>
Child.vue
<script>
export default {
name:"child",
created() {
this.$root.$refs.child = this;
},
methods: {
methodtoexecute() {
alert("hello from child");
}
}
}
</script>

Vue parent-child emit function is breaking v-model binding

Working on fixing a bug in someone else's code, so I'm trying to limit what I have to change here.
Seems like when I use $emit functionality to run functions between child and parent components, v-model binding is being lost in my components.
There is a parent component:
ParentComponent.vue
<template>
<child-component v-bind:items="this.items"
v-on:event_child="this.eventUpdater">
</child-component>
<template>
<script>
import ChildComponent from './ChildComponent.vue';
export default {
components: {
'child-component': ChildComponent
},
methods: {
getItemDetails() {
//...ajax request that loads item details for page.
},
eventUpdater: function(id) {
this.getItemDetails();
}
}
}
</script>
Then, there is a child component:
ChildComponent.vue
<template>
<div v-for="item in items">
<input v-model="item.itemId">
</div>
<button v-on:click="updateItems">update</button>
</template>
<script>
export default {
props: ['items'],
methods: {
updateItems() {
//...ajax call that updates items.
this.emitWhat();
},
emitWhat: function () {
this.$emit('event_child');
}
}
}
</script>
After updating my initial item (which updates fine), I go to update another item, and it seems like the v-model for that item does not work. Is the $emit functionality breaking the v-model binding after initially loading? How do I fix this?
You are using this:
<child-component v-bind:items="this.items"
v-on:event_child="this.eventUpdater">
but should use this:
<child-component v-bind:items="items"
v-on:event_child="eventUpdater">
Removed this..
Also I didn't find items as data property in parent component.
Upd.
Also if you define id parameter in eventUpdater: function(id) method you should emit it like this:
<template>
<div v-for="item in items">
<input v-model="item.itemId">
</div>
<button v-on:click="updateItems(item.itemId)">update</button>
</template>
updateItems(id) {
//...ajax call that updates items.
this.emitWhat(id);
},
emitWhat: function (id) {
this.$emit('event_child', id);
}
Upd.2
Also you have v-model on item.itemId which could be a problem:
<input v-model="item.itemId">
You could consider to bind v-model to newItems like this:
<input v-model="newItems[itemId]">
data(){
return {
newItems: [],
//...
};
}

How to pass data from child to parent component in vue?

I am using Vue.Js where i am calling my child component multiple times from parent. Which means there are separate instance created for all the different call. Data "json" will contain seperate value for all the different instance.
Now i want to fetch data present in variable json in all the instance of child component from parent component.
[Code]
Parent component
<div v-for="(value, index) in inputs" :key="index++">
<ChildComponent :componentcount="index" ></ChildComponent>
</div>
Child Component
<template>
<div id="hello">
<div>
<v-text-field :id="'ComponentHeader_' + $attrs.componentcount" v-model="header"
class="headertag" label="Child Tag" #change="createJson" outlined>
</v-text-field>
</div>
</div>
</template>
<script>
export default {
data(){
return{
json:"",
}
}
}
You can use $emit method for this purpose.
v-on directive captures the child components events that is emitted by $emit
Child component triggers clicked event:
export default {
methods: {
onClickButton (event) {
this.$emit('clicked', 'someValue')
}
}
}
Parent component receive clicked event:
<div>
<child #clicked="onClickChild"></child>
</div>
export default {
methods: {
onClickChild (value) {
console.log(value) // someValue
}
}
}

Change a property's value in one component from within another component

I'm trying to wrap my head around hoe Vue.js works, reading lots of documents and tutorials and taking some pluralsight classes. I have a very basic website UI up and running. Here's the App.vue (which I'm using kinda as a master page).
(To make reading this easier and faster, look for this comment: This is the part you should pay attention to)...
<template>
<div id="app">
<div>
<div>
<CommandBar />
</div>
<div>
<Navigation />
</div>
</div>
<div id="lowerContent">
<!-- This is the part you should pay attention to -->
<template v-if="showLeftContent">
<div id="leftPane">
<div id="leftContent">
<router-view name="LeftSideBar"></router-view>
</div>
</div>
</template>
<!-- // This is the part you should pay attention to -->
<div id="mainPane">
<div id="mainContent">
<router-view name="MainContent"></router-view>
</div>
</div>
</div>
</div>
</template>
And then in the same App.vue file, here's the script portion
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
import CommandBar from './components/CommandBar.vue';
import Navigation from './components/Navigation.vue';
#Component({
components: {
CommandBar,
Navigation,
}
})
export default class App extends Vue {
data() {
return {
showLeftContent: true // <--- This is the part you should pay attention to
}
}
}
</script>
Ok, so the idea is, one some pages I want to show a left sidebar, but on other pages I don't. That's why that div is wrapped in <template v-if="showLeftContent">.
Then with the named <router-view>'s I can control which components get loaded into them in the `router\index.ts\ file. The routes look like this:
{
path: '/home',
name: 'Home',
components: {
default: Home,
MainContent: Home, // load the Home compliment the main content
LeftSideBar: UserSearch // load the UserSearch component in the left side bar area
}
},
So far so good! But here's the kicker. Some pages won't have a left side bar, and on those pages, I want to change showLeftContent from true to false. That's the part I can't figure out.
Let's say we have a "Notes" component that looks like this.
<template>
<div class="notes">
Notes
</div>
</template>
<script lang="ts">
import { Component, Prop, Vue } from 'vue-property-decorator';
#Component
export default class Notes extends Vue {
data() {
return {
showLeftContent: false // DOES NOT WORK
}
}
}
</script>
Obviously, I'm not handling showLeftContent properly here. It would seem as if the properties in data are scoped only to that component, which I understand. I'm just not finding anything on how I can set a data property in the App component and then change it in a child component when that child is loaded through a router-view.
Thanks!
EDIT:
I changed the script section of the Notes component from:
<script lang="ts">
import { Component, Prop, Vue } from 'vue-property-decorator';
#Component
export default class Notes extends Vue {
data() {
return {
showLeftContent: false // DOES NOT WORK
}
}
}
</script>
to:
<script lang="ts">
import { Component, Prop, Vue } from 'vue-property-decorator';
#Component
export default class Notes extends Vue {
mounted() {
this.$root.$data.showLeftContent = false;
}
}
</script>
And while that didn't cause any compile or runtime errors, it also didn't have the desired effect. On Notes, the left side bar still shows.
EDIT 2:
If I put an alert in the script section of the Notes component:
export default class Notes extends Vue {
mounted() {
alert(this.$root.$data.showLeftContent);
//this.$root.$data.showLeftContent = false;
}
}
The alert does not pop until I click on "Notes" in the navigation. But, the value is "undefined".
EDIT 3:
Struggling with the syntax here (keep in mind this is TypeScript, which I don't know very well!!)
Edit 4:
Inching along!
export default class App extends Vue {
data() {
return {
showLeftContent: true
}
}
leftContent(value: boolean) {
alert('clicked');
this.$root.$emit('left-content', value);
}
}
This does not result in any errors, but it also doesn't work. The event never gets fired. I'm going to try putting it in the Navigation component and see if that works.
As it says on #lukebearden answer you can use the emit event to pass true/false to the main App component on router-link click.
Assuming your Navigation component looks like below, you can do something like that:
#Navigation.vue
<template>
<div>
<router-link to="/home" #click.native="leftContent(true)">Home</router-link> -
<router-link to="/notes" #click.native="leftContent(false)">Notes</router-link>
</div>
</template>
<script>
export default {
methods: {
leftContent(value) {
this.$emit('left-content', value)
}
}
}
</script>
And in your main App you listen the emit on Navigation:
<template>
<div id="app">
<div>
<Navigation #left-content="leftContent" />
</div>
<div id="lowerContent">
<template v-if="showLeftContent">
//...
</template>
<div id="mainPane">
//...
</div>
</div>
</div>
</template>
<script>
//...
data() {
return {
showLeftContent: true
}
},
methods: {
leftContent(value) {
this.showLeftContent = value
}
}
};
</script>
A basic approach in a parent-child component relationship is to emit events from the child and then listen and handle that event in the parent component.
However, I'm not sure that approach works when working with the router-view. This person solved it by watching the $route attribute for changes. https://forum.vuejs.org/t/emitting-events-from-vue-router/10136/6
You might also want to look into creating a simple event bus using a vue instance, or using vuex.
If you'd like to access the data property (or props, options etc) of the root instance, you can use this.$root.$data. (Check Vue Guide: Handling Edge)
For your codes, you can change this.$root.$data.showLeftContent to true/false in the hook=mounted of other Components, then when Vue creates instances for those components, it will show/hide the left side panel relevantly.
Below is one demo:
Vue.config.productionTip = false
Vue.component('child', {
template: `<div :style="{'background-color':color}" style="padding: 10px">
Reach to root: <button #click="changeRootData()">Click me!</button>
<hr>
<slot></slot>
</div>`,
props: ['color'],
methods: {
changeRootData() {
this.$root.$data.testValue += ' :) '
}
}
})
new Vue({
el: '#app',
data() {
return {
testValue: 'Puss In Boots'
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.js"></script>
<div id="app">
<h2>{{testValue}}</h2>
<child color="red"><child color="gray"><child color="green"></child></child></child>
</div>

Call a function in imported child component on click from a parent element?

Is it possible to call a function in an imported component from "parent element"? Because I have a few modules that I wanna have in my app and show them dynamically in my app. I though if I import a component like Header in my main App.vue folder it will recognize the function from the main App.vue folder.
Example of App.vue:
<template>
<div>
<div class="m-grid m-grid--hor m-grid--root m-page">
<!-- <loader></loader> -->
<mobile-menu-partial></mobile-menu-partial>
<header-partial></header-partial>
<div :is="currentComponent"></div>
<div v-show="!currentComponent" v-for="component in componentsArray" :key="component.id">
<button #click="swapComponent(component)">{{component}}</button>
</div>
<button #click="swapComponent(null)">Close</button>
<footer-partial></footer-partial>
</div>
</div>
</template>
<script>
import Loader from '#/components/partials/Loader.vue'
import MobileMenu from '#/components/partials/MobileMenu.vue'
import Header from '#/components/partials/Header.vue'
import Footer from '#/components/partials/Footer.vue'
export default {
data () {
return {
currentComponent: null,
componentsArray: ['dashboard', 'schedule', 'locations', 'mileage']
}
},
name: 'App',
components: {
'loader': Loader,
'mobile-menu-partial': MobileMenu,
'header-partial': Header,
'footer-partial': Footer
},
methods: {
swapComponent: function (component) {
console.log('component', component)
this.currentComponent = component
if (component === null) {
console.log('anywhere_main')
this.$router.push('/')
} else {
this.$router.push('/' + component)
}
}
}
}
</script>
<style>
</style>
So can I have access of the function swapComponent in my header-partial? Because there are my modules for opening.
Calling child component's method from parent component
Say you have a child-comp component and want to call its childMethod method from the parent.
Use ref="someVariableName" on the parent's template and this.$refs.someVariableName on the parent's JavaScript.
Parent's template:
<div id="app>
<child-comp ref="myChild"></child-comp>
<button #click="callChildMethod">GO</button>
</div>
Parent's JavaScript code:
{
data: // ...
// ...
methods: {
callChildMethod: function () {
this.$refs.myChild.childMethod();
}
}
Calling parent's method from child
Emit an event in <child-comp> and listen to it in parent.
Parent's template (listening to an event):
<div id="app>
<child-comp #eventname="parentsMethod"></child-comp>
</div>
Notice that #eventname="parentsMethod" is the same as v-on:eventname="parentsMethod".
Parent's JavaScript code:
{
data: // ...
// ...
methods: {
parentsMethod: function (event) {
console.log('parent called', event);
}
}
Child's JavaScript code:
{
data: // ...
// ...
methods: {
someChildMethod: function () {
this.$emit('eventname', {some: 'object value'});
}
}
Now whenever, at the child, you call someChildMethod it will trigger the event and, since the parent is listening to it, it will execute parentsMethod in the parent.

Categories

Resources