How to create a custom link component in Vue.js?

10,855

Router link takes a tag attribute, that you can use to turn it into any element you like. An example would be...

<router-link tag="tr" :to="'/messages/' + MAIL_ID">{{ MAIL_TITLE }}</router-link>
Share:
10,855
redOctober13
Author by

redOctober13

JavaScript developer, User Experience Designer, UI Designer, agilist, problem solver. I've been an avid reader since my youth; I write decently (or so I'm told); I do presentations on things from Git version control to slideware design to philosophy, theology, and extraterrestrial life. I'm a disciple of Jesus working to be more like him in how I relate to others. I'm a father of 3, husband of one hardworking entrepreneurial wife. I'm part of the F3 and CrossFit communities. My hobbies include woodworking, BMX, mountain biking, gaming, reading, knives, the shooting sports, and blacksmithing (hopefully). I care about making good products and helping people become more efficient, whether that's through the apps I help build, or something as simple as navigating around Excel.

Updated on June 11, 2022

Comments

  • redOctober13
    redOctober13 almost 2 years

    This seems like your run-of-the-mill master/detail use case, but the examples in the Vue docs stop short of examples for this. I have a Mail Folder page (route /:mailbox_id) that displays a table of emails by Date, Subject, etc., and I want a nested route (/:message_id) that shows the email's text when the user clicks on a row.

    I was able to do this in Ember (recreating this) because Ember makes a JavaScript onClick function to handle the routing and lets you set the HTML element to render, and then you just pass whatever objects to the child route.

    But I'm new to Vue.js, and I've been going through the docs but can't grok how to accomplish the same thing. I can't figure out how to either create a custom link component, or how to use the built-in Vue <router-link>component (because I need it to be a <tr> instead of <a>) to both go to the child route, and pass along the contents of the message to it so it can be shown.

    If it helps, here's some code:

    The Router

    export default new Router({
      routes: [
        {
          path: '/:id',
          name: 'mailbox',
          component: Mailbox,
          props: true,
          children: [
            {
              path: 'mail/:id',
              name: 'mail',
              component: Mail,
              props: true
            }
          ]
        }
      ]
    })
    

    Component: Mailbox.vue

    <template>
      <div>
        <table>
          <tr>
            <th>Date</th>
            <th>Subject</th>
            <th>From</th>
            <th>To</th>
          </tr>
          <Mail-List-Item v-for="message in messages" :key="message.id" v-bind:message="message"/>
        </table>
        <router-view></router-view>
      </div>
    </template>
    
    <script>
      import MailListItem from './Mail-List-Item'
    
      export default {
        components: { 'Mail-List-Item': MailListItem },
        name: 'Mailbox',
        props: ['messages']
      }
    </script>
    

    Component: Mail.vue

    <template>
      <div class="mail">
        <dl>
          <dt>From</dt>
          <dd>{{mail.from}}</dd>
          <dt>To</dt>
          <dd>{{mail.to}}</dd>
          <dt>Date</dt>
          <dd>{{messageDate}}</dd>
        </dl>
        <h4>{{mail.subject}}</h4>
        <p>{{mail.body}}</p>
      </div>
    </template>
    
    <script>
    export default {
      props: ['message', 'messageDate']
    }
    </script>
    

    Component: Mail-List-Item.vue

    <template>
        <V-Row-Link href="mail" mailid="message.id" message="message">
          <td>{{messageDate}}</td>
          <td>{{message.subject}}</td>
          <td>{{message.from}}</td>
          <td>{{message.to}}</td>
        </V-Row-Link>
    </template>
    
    <script>
      var moment = require('moment')
      import VRowLink from './V-Row-Link'
    
      export default {
        name: 'Mail-List-Item',
        props: ['message'],
        components: { VRowLink },
        data: function () {
          return {messageDate: moment(this.message.date).format('MMM Do')}
        }
      }
    </script>
    

    Component: V-Row-Link.vue (much of this copied from this repo)

    <template lang="html">
      <tr
        v-bind:href="href"
        v-on:click="go"
        >
          <slot></slot>
      </tr>
    </template>
    
    <script>
    import routes from '../Router'
    
    export default {
      props: ['href', 'mailid', 'message'],
      methods: {
        go (event) {
          this.$root.currentRoute = this.href
          window.history.pushState(
            null,
            routes[this.href],
            this.href
          )
        }
      }
    }
    </script>