DEV Community

Cover image for Vue Slots and Reusable components
crisarji
crisarji

Posted on

Vue Slots and Reusable components

Vue Slots and Reusable components

Hello developer pal!, glad to see you here.

In this post, we will take a look at the way a simple feature as it is slot in vue could spare a lot of time and effort when having the same components displaying different data.

Sometimes, we need to replicate the same task with different data, for instance, you have a design system which determines that every single list element throughout the site needs to be consistent, this means having the same height, width, font, etc. Sounds fair!

For accomplishing this, there are 2 options:

  1. Copy + Paste the same listing component in multiple places; the fallback of this approach comes when, for example, the font of the list item needs to be updated; if you, by chance, have in the project 1, 2 or 3 list items, you can manually make the change, all good!, but what happens when your project has a whole bunch of listing components?, it would be required go one by one and make the change; the effort on dev, qa, and the risk of letting one scenario out of the scope is high.

  2. Create a Reusable Component, this will allow it to make the change in one only place and affect all of them at once!, here the slot concept comes handy, when using a slot, you can have the same look and feel, behavior, properties and others available for all the listing components.

Show Me The Code

The steps to be focused on are:

  1. Having 1 reusable listing component
  2. Having different view components, injected with different data
  3. Having different view components, displaying different elements

Let me share to you the GitHub code here.

1. Having 1 reusable listing component

How would it look a slot for the example purposes?:

<template>
  <div>
    <ul>
      <li v-for="(element, index) in elements" :key="element.id">
        <slot name="element" v-bind:element="element" v-bind:index="index"></slot>
      </li>
    </ul>
  </div>
</template>
Enter fullscreen mode Exit fullscreen mode

In the snippet above, there is a v-for directive which loops over the array in a property named elements; what it is happening is that every single element, and its index, are available to be part of the template, the slot has the option for binding values, these are available as soon as the slot is invoked.

This simple is to have a reusable component with slots!, the html elements can be modified as required: add css, new attributes, new binding props, everything will be ready to be used when invoked the component(check the GitHub repo for a nicer experience including Tailwind).

2. Having different view components, injected with different data

For the sake of the exercise, 3 different arrays are present for feeding the view components:

export const agents = [
  { id: '07531267-D', name: 'Remedios Carmona', calls: 12, deals: 5 },
  { id: '91958619', name: 'Dalal Heidema', calls: 20, deals: 12 },
  { id: 'NNaN31539321', name: 'Julien Legrand', calls: 25, deals: 17 },
  { id: '1NNaN60472038', name: 'Karina da Cunha', calls: 33, deals: 25 },
  { id: '060469-1435', name: 'Evelyn Scheerer', calls: 40, deals: 35 },
];

export const supervisors = [
  { id: '75687', name: 'Idelso Gonçalves', agents: 10, teams: 1, gender: 'male' },
  { id: '2NNaN64983816', name: 'Cassandra Leroy', agents: 20, teams: 2, gender: 'female' },
  { id: 'N901057Z', name: 'Arron Johnston', agents: 30, teams: 3, gender: 'male' },
  { id: '18066349671', name: 'Siham Reitan', agents: 40, teams: 4, gender: 'female' },
  { id: '48926083', name: 'Mariam Linde', agents: 50, teams: 5, gender: 'female' },
];

export const managers = [
  { id: 'NaNNA831undefined', name: 'Niilo Keranen', department: 'IT', gender: 'male' },
  { id: '2NNaN40789264', name: 'Leana Deschamps', department: 'Operations', gender: 'female' },
  { id: '283707860', name: 'Irma Boyd', department: 'HHRR', gender: 'female' },
  { id: '290471', name: 'Nicole Oehme', department: 'ACC', gender: 'female' },
  { id: '1NNaN44873525', name: 'Antonin Rey', department: 'Facilities', gender: 'male' },
];
Enter fullscreen mode Exit fullscreen mode

As you can notice, the views will be Agents, Supervisors and Managers; as aforementioned, this items must be listed up following a design system specifications(width, height, font, typo) for keeping the consistency.

Let's suppose that the design system ask for something like these:

Agents view:

Supervisors view:

Managers view:

After taking a look at the mocking data and the requirements, what is changing are the fields to display, the order, and the background, since the Copy+Paste approach is discarded, the other possible way to go is the Reusable Components.

3. Having different view components, displaying different elements

Let's dig a bit into the implementation of the Reusable Component and slots.

The very first view is the one for Agents, we need to import the reusable component, register it, add an input property for feeding the list of elements and render it out(skipped steps in here, you can check the source code for more info), let focus in the template

Agents view:

<template>
  <custom-list :listElements="listElements" itemStyling="justify-between bg-gray-300">
    <template v-slot:element="{ element }">
      <div class="flex w-1/5"></div>
      <div class="flex w-2/5"></div>
      <div class="flex w-2/5"></div>
    </template>
  </custom-list>
</template>
Enter fullscreen mode Exit fullscreen mode

A couple of interesting things from above snippet:

  • custom-list(our reusable component) is expecting for the list of elements and an item styling, also, it is accessing the element binding, so the data to be display is dynamic.

What about the second view?

Supervisors view:

<template>
  <custom-list :listElements="listElements" itemStyling="justify-between bg-blue-300">
    <template v-slot:element="{ element }">
      <div class="flex w-2/5"></div>
      <div class="flex w-1/5"></div>
      <div class="flex justify-end w-2/5"></div>
    </template>
  </custom-list>
</template>
Enter fullscreen mode Exit fullscreen mode

This is pretty similar to the previous one, the biggest difference is that a different color is send to affect the background(in case you are not familiar with Tailwind, it is this bg-blue-300), and the order of the divs(the avatar from the mocks will be placed at the center)

Last but not least:

Managers view:

<template>
  <custom-list :listElements="listElements" itemStyling="justify-between bg-green-300">
    <template v-slot:element="{ element, index }">
      <div class="flex w-2/5"></div>
      <div class="flex justify-center w-2/5"></div>
      <div class="flex w-1/5"></div>
    </template>
  </custom-list>
</template>
Enter fullscreen mode Exit fullscreen mode

Also similar to the other 2(that's the idea), but changing the background color again, the order of the elements to be displayed(avatar at the end) and also including the other binding property declared in the reusable component, the index.

After all the steps above, this is the output:

Notes:

  1. You can find the repo for Vue2 and Vue3
  2. Feel free to fork it and/or import it in an online code editor(I tried but there seems to be a known error )

Challenge: The CustomList component could be register globally, in case you want to stop importing it throughout the site

Conclusion

As shown above, the slots could spare some precious development time, help with the scalability and be customized as required; maybe you could have a better way to do it, let's discuss in a thread below!

Thanks for reading!

Top comments (2)

Collapse
 
freakjacker profile image
freak-jacker

Slot Component aways change the way of databinding
But It's a good way to Packaging

Collapse
 
crisarji profile image
crisarji

Indeed!, and used in a MonoRepo structure can save a lot of time..