appu
appu

Reputation: 481

VueJS: Access parent prop in child when the child is visited directly (using vue-router) without a centralized store (vuex or eventBus)

In this codesandbox demo, the parent home component has a child addItem component. Parent's passes array (as prop) to the child and so the child is able to add item to the parent's list i.e. the normal way of passing data from parent -> child i.e. via props :fruitsArr="fruits".

Main.js

import Vue from "vue";
import VueRouter from "vue-router";
import App from "./App.vue";
import Home from "./components/Home.vue";
import addItem from "./components/addItem.vue";

Vue.use(VueRouter);

var router = new VueRouter({
  mode: "history",
  routes: [
    { path: "", component: Home, name: "Home" },
    {
      path: "/addItem",
      component: addItem,
      name: "Add Item"
    }
  ]
});

Vue.config.productionTip = false;

new Vue({
  router,
  render: h => h(App)
}).$mount("#app");

App.vue

<template>
  <div id="app">
    <app-header></app-header>
    <br>
    <router-view></router-view>
  </div>
</template>

<script>
import appHeader from "./components/common/appHeader";

export default {
  name: "App",
  components: {
    appHeader
  }
};
</script>

appHeader.vue

<template>
  <ul class="nav nav-pills">
    <router-link tag="li" to="/" active-class="active" exact>
      <a>Home</a>
    </router-link>
    <router-link tag="li" to="/addItem" active-class="active">
      <a>Add Item</a>
    </router-link>
  </ul>
</template>

Home.vue

<template>
  <div>
    <addItem :fruitsArr="fruits"/>
    <h3>Fruits List</h3>
    <ul>
      <li v-for="(fruit, idx) in fruits" :key="idx">{{fruit}}</li>
    </ul>
  </div>
</template>

<script>
import addItem from "./addItem";

export default {
  name: "App",
  data() {
    return {
      fruits: ["Apple", "Mango", "Orange", "PineApple"]
    };
  },
  components: {
    addItem
  }
};
</script>

addItem.vue

<template>
  <div class="input-wrapper">
    Enter Fruit Name:
    <input type="text" v-model="fruitItem">
    <button @click="addFruit">Add Fruit</button>
  </div>
</template>

<script>
export default {
  data() {
    return {
      fruitItem: ""
    };
  },
  props: ["fruitsArr"],
  methods: {
    addFruit() {
      this.fruitsArr.push(this.fruitItem);
    }
  },
};
</script>

The problem is when the child is visited directly using router-link (vue-router) then parent's prop :fruitsArr="fruits" is not available.

Can anybuddy tell me, how to or the correct way to pass parent's prop to the child when the current route is the child, such that child is able to add item to the parent's list. I'd prefer a solution WITHOUT using VueX or any other centralized store such as eventBus.

Thanks

Upvotes: 0

Views: 2464

Answers (2)

Steven Spungin
Steven Spungin

Reputation: 29109

There are a couple of solutions.

Provide/Inject

You can use provide/inject to send data down the child hierarchy. This is the recommended way to share data from parent to non-immediate child when the communication is only needed on a single parent-child branch, and you do not want to go with Vuex.

https://v2.vuejs.org/v2/api/#provide-inject

Meta Field

You can store items in the route's meta fields and initialize it in a navigation guard. Then it is available in all components.

https://router.vuejs.org/guide/advanced/navigation-guards.html

const router = new VueRouter({
  routes: [
    {
      path: '/foo',
      component: Foo,
      children: [
        {
          path: 'bar',
          component: Bar,
          // a meta field
          meta: { requiresAuth: true }
        }
      ]
    }
  ]
})

With that said, the Vue way is to emit events up and receive properties down, so try to work with that architecture if your use-case allows.

Upvotes: 1

Ivan Klochkov
Ivan Klochkov

Reputation: 702

Steven Spungin`s answer is good. But you shall take into account that provide/inject data is not reactive.

That being said - you need to make some sort of centralized "source of truth". If you don't want to use Vuex - simpliest solution is to declare one more Vue instance and store your global data there. It would be reactive.

const myStore = new Vue({
  data:{
    fruits: ["Apple", "Mango", "Orange", "PineApple"]
  }
})
export myStore;

and in your component

import {myStore} from 'somewhere';

//to use prop in your template - proxy it using computed prop
....
computed:{
  fruits(){
    return myStore.fruits;
  }
}
....

Upvotes: 0

Related Questions