4/10/2022»»Sunday

Vue 3 Slots

4/10/2022
    44 - Comments

Transcript from the 'Slots' Lesson

[00:00:00]
>> Slots, slots are really fun. So far we've been passing things with props and saying like, okay, render this thing but bypassing props. But there are instances where you just need to render a little bit of content differently. So you can pass some content into a component with ease.

[00:00:17] It's just waiting there for that content. So a modal is a pretty good example of this. Typically, with a modal, you've got something that is going to always function, similarly, where it's got a certain presentation style, it's gonna open, it's gonna close, but what's inside of it might change, right?

Vue users should use the official @vitejs/plugin-vue-jsx plugin, which provides Vue 3 specific features including HMR, global component resolving, directives and slots. If not using JSX with React or Vue, custom jsxFactory and jsxFragment can be configured using the esbuild option. For example for Preact.

[00:00:34] The way that you're using that modal might change because you want different content to be displayed. So here, it's Here I am, Here I am. And, inside of here, if we look at this we've got is showing, so we've got a v-if v-else work, basically toggling. This is showing, and we've got our template, Here.

  • If a slot is defined without a name attribute then any content which is placed within component tags not specifying a slot attribute will be placed into that slot. See the multi insertion example on the Vue.js official docs.
  • This page assumes you’ve already read the Components Basics.Read that first if you are new to components. Vue implements a content distribution API that’s modeled after the current Web Components spec draft, using the slot element to serve as distribution outlets for content.
  • Bonus B: Vue 3 In their essence, scoped slots are just a more powerful superset of 'regular' slots. But since introducing them needed a slightly different API, Vue 2 had to distinguish between $slots and $scopedSlots. Vue 3 took the chance and unified both APIs.
  • In this article, we’ll look at how to test Vue 3 apps by writing a simple app and testing it. Testing Slots with Render Functions We can test slots with render functions and single-file components. For example, we can write: Header.vue.

[00:01:00] Inside of our template for this kind of script tag, we're putting in a thing called a slot. What that allows me to do is inside of that component rather than just saying at modal and closing off at modal, I can put whatever I want in here. I could put h2s, I can make a p tag and say like, hey there, everyone, I am more and p tag, I'm not passing that in with a prop.

[00:01:29] I'm able to put whatever content I want into that slot and it will just live as content. Modals are a great use case for this because that's typically what we do. We like want the same basic functionality, the same look and feel and everything. And then we wanna pass a bit of different content.

[00:01:46] So that's typically how we work with that. So we're passing in this slot, I'm gonna show you my veritable slot machine. [LAUGH] So we've got these slots to populate content. We have some pieces that are hard coded, but we were saying in this first one, we want slot number one.

[00:02:06] In the second one, we want slot number two, and we can put more information into slot number one, slot number two, both inside of that same app child component. And so here, we are like we can use slots to populate content. This is slot number one, this is slot number two, and both of them have this terrible joke of it's so we can hard code content in that component as well.

[00:02:31] But we can pass in whatever information we want. And it can be any tags that we want as well. Whereas with props, we're not necessarily passing in all of the p tags and all of this stuff too. So you can also have some defaults. So if you know that most of the modals on your site are going to be saying like, you're signed in, then you could, inside that slot, say, you're signed in and then replace it when you need to.

[00:02:58] So that's pretty cool. You can have more than one or if you just wanna keep things tidy by naming them, which I suggest, you can say, template v slot header. And then pass that in, nd then in the component itself, you would say slot neat, you give it a name.

[00:03:13] You say name is header, and then you say that template v slot. I'll show you an example of that. So if I have my Taco blog, because I blog every day about tacos, I don't really but I should. And then we've got this app child. I can say template v-slot header is the taco blog.

[00:03:34] The v-slot default is all of this information that I have. And then the v-slot footer has generated by Taco Ipsum. But you didn't know there was Lorem ipsum for tacos. And if we look at the component itself, we have a header that's just HTML, you don't have to put it in that header.

[00:03:55] And we've got our slot name header, we've got our slot name footer. But, here's the tricky part. If we have a slot that is unnamed, we can still pass information in. This was called v-slot default. So we had two named headers, or two named slots, and then a default slot that we can put this in.

[00:04:17] Another note between v2 and v3, this template v-slot is new. That's not how we used to work with it in v2. It's a way for us we used to use scoped slots slightly differently than regular slots, and there's a merging of this to make it a little bit more simple.

[00:04:34] So, here is all of this. And then if you wanted to see the rendered content, let's go back to the, my slides are getting a little slow here, sorry about that. If you want to see what was rendered to the page, like what was actually created, we have that header that I mentioned is just HTML that was placed in side of here.

[00:04:55] The main with all of these p tags in the footer, so it's rendered to the page like this and you can add whatever information you want. We could also do something like make a wine label if we wanted to. So I could say like, wine time, which is my favorite time.

[00:05:15] And then we've got a white label and a black label and I can even pick a color here if we wanted to, maybe something bright red, I can change the font. All of this stuff, I can add some flourishes, I can add a background. And I can change the image opacity if it's like conflicting with my background.

[00:05:37] I can change the placement of the label up and down and up and down. These are all form things that you did before with v-modal. And what's tricky here is actually that that entire label is passed in as a slot. Really what I have is this SVG and there's a lot of number garbage here, don't worry about that.

[00:06:01] That's coordinate system in SVG, like working with, rendering all of these different pieces of the SVG. But here is the magic. We have this black one and this white one, we're passing all the information of the SVG into a slot. And the reason why we might wanna do this is because we can have different styles for each one of them.

[00:06:28] So I can say like black label is fill black, black bottle and fill white and all of these things because I gave it a different class. Now, when we move to single file components, you can actually create a scoped tag for this. So that's really exciting. But here we're just doing what some classes, right, we're changing the way that it is outputting based on that.

[00:06:51] And, there's one more thing. There's a directive that you didn't see before. The hidden secret extra directive which is, this allows for us to use dynamic components. So, when we have this selected at back, the component is going to be whatever I selected here. And I have v-modal that allows me to select appBlack or appWhite and it's gonna switch out that component based on which one I want.

[00:07:23] So now we can pass all of that into a slot and we can also change all of those pieces based on that directive. So here we have that input type, the modal label color. So what we have is, when we click this button of selected, we say select appBlack or we say at click appWhite, cool.

[00:07:51] Let's talk about the keep-alive and the is directive. Let's do a little bit of a deeper dive into is directive because it is really cool and creating dynamic components is really cool. So, I mentioned before that we could do component is selected and we can pass in whatever we want and we can switch out components as we see fit, which is really nice.

Vue 3 Slots Games

[00:08:12] We also have available to us this thing called keep-alive that you can wrap that component in. So, let's say we have this home component and this post component. And in posts, if I click on one of these, I have Doggo Ipsum, Hipster Ipsum, Cupcake Ipsum, excellent, and for each one of these I have a little bit more information.

[00:08:38] I have a bunch of things about the cupcakes, actually, let's open this one in a CodePen too. So we've got our current tabs, we've got home and posts. And then we've got our computed property which is showing which one we are using the home or the posts. And then in here, by clicking on this, we're creating this piece.

[00:09:05] So let's go look at this. At click his currenttab is tabs, and the component is CurrentTabComponent. So whichever one we clicked on will bring up cupcake or hipster or doggo, but we wrapped it in a keep-alive. And what that allows us to do is I'm on Doggo Ipsum right now.

[00:09:26] If I go back home and I come back, there's my Doggo Ipsum. If I remove the keep-alive, this component will mount and unmount and I'll lose all that state. So if I say Hipster Ipsum, I go back here, I go back, it's not there anymore. Because the component is completely mounting and unmounting, it doesn't remember which one you selected.

[00:09:52] Whereas with this keep-alive, it's saying remember which one I had selected. Remember which piece I had dynamically bound. So that's pretty cool. That means you can create dynamic components that remember themselves quite easily. So there's more information about scoped slots and there's tons more information in the documentation.

[00:10:18] We have a lot to cover today, so we're not gonna go into any diving into this any further but you can absolutely check more information out there

Slots are a powerful tool for creating reusable components in Vue.js, though they aren’t the simplest feature to understand. Let’s take a look at how to use slots and some examples of how they can be used in your Vue applications.

With the recent release of Vue 2.6, the syntax for using slots has been made more succinct. This change to slots has gotten me re-interested in discovering the potential power of slots to provide reusability, new features, and clearer readability to our Vue-based projects. What are slots truly capable of?

If you’re new to Vue or haven’t seen the changes from version 2.6, read on. Probably the best resource for learning about slots is Vue’s own documentation, but I’ll try to give a rundown here.

What Are Slots?

Slots are a mechanism for Vue components that allows you to compose your components in a way other than the strict parent-child relationship. Slots give you an outlet to place content in new places or make components more generic. The best way to understand them is to see them in action. Let’s start with a simple example:

This component has a wrapper div. Let’s pretend that div is there to create a stylistic frame around its content. This component is able to be used generically to wrap a frame around any content you want. Let’s see what it looks like to use it. The frame component here refers to the component we just made above.

The content that is between the opening and closing frame tags will get inserted into the frame component where the slot is, replacing the slot tags. This is the most basic way of doing it. You can also specify default content to go into a slot simply by filling it in:

So now if we use it like this instead:

Deprecated

The default text of “This is the default content if nothing gets specified to go here” will show up, but if we use it as we did before, the default text will be overridden by the img tag.

Multiple/Named Slots

You can add multiple slots to a component, but if you do, all but one of them is required to have a name. If there is one without a name, it is the default slot. Here’s how you create multiple slots:

We kept the same default slot, but this time we added a slot named header where you can enter a title. You use it like this:

Vue 3 Slots Online Casino

Just like before, if we want to add content to the default slot, just put it directly inside the titled-frame component. To add content to a named slot, though, we needed to wrap the code in a template tag with a v-slot directive. You add a colon (:) after v-slot and then write the name of the slot you want the content to be passed to. Note that v-slot is new to Vue 2.6, so if you’re using an older version, you’ll need to read the docs about the deprecated slot syntax.

Scoped Slots

One more thing you’ll need to know is that slots can pass data/functions down to their children. To demonstrate this, we’ll need a completely different example component with slots, one that’s even more contrived than the previous one: let’s sorta copy the example from the docs by creating a component that supplies the data about the current user to its slots:

This component has a property called user with details about the user. By default, the component shows the user’s last name, but note that it is using v-bind to bind the user data to the slot. With that, we can use this component to provide the user data to its descendant:

To get access to the data passed to the slot, we specify the name of the scope variable with the value of the v-slot directive.

There are a few notes to take here:

  • We specified the name of default, though we don’t need to for the default slot. Instead we could just use v-slot='slotProps'.
  • You don’t need to use slotProps as the name. You can call it whatever you want.
  • If you’re only using a default slot, you can skip that inner template tag and put the v-slot directive directly onto the current-user tag.
  • You can use object destructuring to create direct references to the scoped slot data rather than using a single variable name. In other words, you can use v-slot='{user}' instead of v-slot='slotProps' and then you can use user directly instead of slotProps.user.

Taking those notes into account, the above example can be rewritten like this:

A couple more things to keep in mind:

  • You can bind more than one value with v-bind directives. So in the example, I could have done more than just user.
  • You can pass functions to scoped slots too. Many libraries use this to provide reusable functional components as you’ll see later.
  • v-slot has an alias of #. So instead of writing v-slot:header='data', you can write #header='data'. You can also just specify #header instead of v-slot:header when you’re not using scoped slots. As for default slots, you’ll need to specify the name of default when you use the alias. In other words, you’ll need to write #default='data' instead of #='data'.

There are a few more minor points you can learn about from the docs, but that should be enough to help you understand what we’re talking about in the rest of this article.

What Can You Do With Slots?

Slots weren’t built for a single purpose, or at least if they were, they’ve evolved way beyond that original intention to be a powerhouse tool for doing many different things.

Reusable Patterns

Components were always designed to be able to be reused, but some patterns aren’t practical to enforce with a single “normal” component because the number of props you’ll need in order to customize it can be excessive or you’d need to pass large sections of content and potentially other components through the props. Slots can be used to encompass the “outside” part of the pattern and allow other HTML and/or components to placed inside of them to customize the “inside” part, allowing the component with slots to define the pattern and the components injected into the slots to be unique.

For our first example, let’s start with something simple: a button. Imagine you and your team are using Bootstrap*. With Bootstrap, your buttons are often strapped with the base `btn` class and a class specifying the color, such as `btn-primary`. You can also add a size class, such as `btn-lg`.

VueVue

* I neither encourage nor discourage you from doing this, I just needed something for my example and it’s pretty well known.

Let’s now assume, for simplicity’s sake that your app/site always uses btn-primary and btn-lg. You don’t want to always have to write all three classes on your buttons, or maybe you don’t trust a rookie to remember to do all three. In that case, you can create a component that automatically has all three of those classes, but how do you allow customization of the content? A prop isn’t practical because a button tag is allowed to have all kinds of HTML in it, so we should use a slot.

Now we can use it everywhere with whatever content you want:

Of course, you can go with something much bigger than a button. Sticking with Bootstrap, let’s look at a modal, or least the HTML part; I won’t be going into functionality… yet.

Vue 3 Slots No Deposit

Now, let’s use this:

The above type of use case for slots is obviously very useful, but it can do even more.

Reusing Functionality

Vue components aren’t all about the HTML and CSS. They’re built with JavaScript, so they’re also about functionality. Slots can be useful for creating functionality once and using it in multiple places. Let’s go back to our modal example and add a function that closes the modal:

Now when you use this component, you can add a button to the footer that can close the modal. Normally, in the case of a Bootstrap modal, you could just add data-dismiss='modal' to a button, but we want to hide Bootstrap specific things away from the components that will be slotting into this modal component. So we pass them a function they can call and they are none the wiser about Bootstrap’s involvement:

Renderless Components

And finally, you can take what you know about using slots to pass around reusable functionality and strip practically all of the HTML and just use the slots. That’s essentially what a renderless component is: a component that provides only functionality without any HTML.

Making components truly renderless can be a little tricky because you’ll need to write render functions rather than using a template in order to remove the need for a root element, but it may not always be necessary. Let’s take a look at a simple example that does let us use a template first, though:

This is an odd example of a renderless component because it doesn’t even have any JavaScript in it. That’s mostly because we’re just creating a pre-configured reusable version of a built-in renderless function: transition.

Yup, Vue has built-in renderless components. This particular example is taken from an article on reusable transitions by Cristi Jora and shows a simple way to create a renderless component that can standardize the transitions used throughout your application. Cristi’s article goes into a lot more depth and shows some more advanced variations of reusable transitions, so I recommend checking it out.

For our other example, we’ll create a component that handles switching what is shown during the different states of a Promise: pending, successfully resolved, and failed. It’s a common pattern and while it doesn’t require a lot of code, it can muddy up a lot of your components if the logic isn’t pulled out for reusability.

So what is going on here? First, note that we are receiving a prop called promise that is a Promise. In the watch section we watch for changes to the promise and when it changes (or immediately on component creation thanks to the immediate property) we clear the state, and call then and catch on the promise, updating the state when it either finishes successfully or fails.

Then, in the template, we show a different slot based on the state. Note that we failed to keep it truly renderless because we needed a root element in order to use a template. We’re passing data and error to the relevant slot scopes as well.

And here’s an example of it being used:

We pass in somePromise to the renderless component. While we’re waiting for it to finish, we’re displaying “Working on it…” thanks to the pending slot. If it succeeds we display “Resolved:” and the resolution value. If it fails we display “Rejected:” and the error that caused the rejection. Now we no longer need to track the state of the promise within this component because that part is pulled out into its own reusable component.

So, what can we do about that span wrapping around the slots in promised.vue? To remove it, we’ll need to remove the template portion and add a render function to our component:

There isn’t anything too tricky going on here. We’re just using some if blocks to find the state and then returning the correct scoped slot (via this.$scopedSlots['SLOTNAME'](...)) and passing the relevant data to the slot scope. When you’re not using a template, you can skip using the .vue file extension by pulling the JavaScript out of the script tag and just plunking it into a .js file. This should give you a very slight performance bump when compiling those Vue files.

This example is a stripped-down and slightly tweaked version of vue-promised, which I would recommend over using the above example because they cover over some potential pitfalls. There are plenty of other great examples of renderless components out there too. Baleada is an entire library full of renderless components that provide useful functionality like this. There’s also vue-virtual-scroller for controlling the rendering of list item based on what is visible on the screen or PortalVue for “teleporting” content to completely different parts of the DOM.

I’m Out

Vue’s slots take component-based development to a whole new level, and while I’ve demonstrated a lot of great ways slots can be used, there are countless more out there. What great idea can you think of? What ways do you think slots could get an upgrade? If you have any, make sure to bring your ideas to the Vue team. God bless and happy coding.

Template V Slot

(dm, il)