7
votes

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?

4

4 Answers

3
votes

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 }
                  },
              ]
          },


        ]
      }
    ]
});
4
votes

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.

0
votes

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>
-2
votes

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