简体   繁体   中英

Is there a nice way to pass props to the parent component with Vue router?

I have a form with different steps that all share the same header structure.

The only difference in the header within the different steps is wording in that header which changes along the steps.

I am searching for a way to do this:

In my vue router :

      path: '/form',
      name: 'Form',
      component: Form,
      children: [
        {
          path: 'step1',
          component: FormStep1,
          propsForParent: {
            title: "myTitle In Header In Form Component"
          },
        },
        {
          path: 'step2',
          component: FormStep2,
          propsForParent: {
            title: "myTitle is different In Header In Form Component"
          },
        }
      ]

Therefore when the route is form/step1 I would like my form component to receive the title props set in my children configuration as described above and so on.

I would like to avoid managing that in my parent component and also avoid my child to communicate this information with an event for instance to my parent component or using vuex. I am searching for something nice directly in vue router.

Any idea?

Use route meta data:

path: '/form',
name: 'Form',
component: Form,
children: [
  {
    path: 'step1',
    component: FormStep1,
    meta: {
      title: "myTitle In Header In Form Component"
    },
  },
  {
    path: 'step2',
    component: FormStep2,
    meta: {
      title: "myTitle is different In Header In Form Component"
    },
  }
]

And then in your parent component:

computed: {
  title () { this.$route.meta.title }
}

If you prefer title be passed as a prop to you parent component, use:

routes: [{
  path: '/form',
  name: 'Form',
  component: Form,
  props (route) => {
    return {
      title: route.meta.title
    }
  }
  children: [ ...

You could also make title inheritable. For that you need to use a bit more complicated check:

const matched = route.matched.slice().reverse().find(route => route.meta.title)
matched.meta.title

Note: that slice() seems to do nothing, but it is creating a copy of matched array so we don't modify the original array - removing slice() will ruin your day with debugging.

You are nearly there, just emit from child to the parent with the value of the prop received.

      path: '/form',
      name: 'Form',
      component: Form,
      children: [
        {
          path: 'step1',
          component: FormStep1,
          props: {
            title: "myTitle In Header In Form Component"
          },
        },
        {
          path: 'step2',
          component: FormStep2,
          props: {
            title: "myTitle is different In Header In Form Component"
          },
        }
      ]


//Inside FormStep2 and FormStep1
created() {
    this.$emit('childinit', this.title);
  },


//inside Form
methods: {
    onChildInit( value ){
      this.title = value;
    }
  }

To make things cleaner, consider creating another layer of children inside your router, so you don't have to emit on every child. Here's some code out of a project I'm looking at right now that does something very similar notice the step prop which I'm passing about.

//inside my timelineBase component I listen for onChildInit, set a method to grab value from the child, then use that in the layout to tell pageStepper what section I'm in.

<router-view v-on:childinit="onChildInit" :key="componentKey"></router-view>
<pageStepper :step="pageStepper"></pageStepper>

//code has these props. props: ['mode','step','componentKey'],

This is my routes.

const router = new VueRouter({
    routes: [
      {
        path: '/',
        component: Layout,
        children: [
          {
            path: '',
            component: homepage,
            props: { cssClass: '' },
          },
          {
              name: 'addTimeline',
              path: 'timeline',
              props: { mode:'add', step: 1, componentKey: 0 },
              component: timelineBase,
              children:
              [
                  {
                    path: 'add',
                    component: timeline,
                    props: { mode:'add', step: 1, componentKey: 1},
                  },
                  {
                      name: 'updateTimeline',
                      path: ':id/update',
                      component: timeline,
                      props: { mode:'update', step: 1, componentKey: 2 }
                  },
                  {
                      name: 'addEvent',
                      path: ':id/event/add',
                      component: addevent,
                      props: { mode:'add', step: 2, componentKey: 3 }
                  },
                  {
                      name: 'editEvent',
                      path: ':id/event/edit/:event_id',
                      component: editevent,
                      props: { mode:'update', step: 2, componentKey: 4 }
                  },
                  {
                      name: 'previewTimeline',
                      path: ':id/preview',
                      component: preview,
                      props: { step: 3, componentKey: 5 }
                  },
              ]
          },


        ]
      }
    ]
});

There could be a little improvement to implement it in a more elegant way.

As Squiggs. mentioned, we could emit the childinit in every child component, it would be tedious to write the emit stuff every time in a child component.

However we could use a mixin to solve this problem. We write a mixin that emit the childinit with its props, and import and use this mixin in every child component.

// mixin
export default {
  props: {
    title: {
      type: String,
      default: '',
    },
  },
  created() {
    this.$emit('childinit', this.title)
  },
}

// parent
<template>
  <div class="wrapper">
    <router-view @childinit="childInit"/>
  </div>
</template>

<script>
export default {
  data() {
    return {
      title: '',
    }
  },
  methods: {
    childInit(title) {
      this.title = title
    },
  },
}
</script>


// child
<script>
import TitleMixin from './mixins'

export default {
  mixins: [TitleMixin],
}
</script>

You can also Vuex or localStorage to manage props. By the way, values in Vuex will be cleaned after refreshing.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM