Vue Js Slot Props
Vue 2.6 is released with new syntax for Slots using v-slot
directive. In this tutorial, we’re gonna show you:
- Slot props allow us to turn slots into reusable templates that can render different content based on input props. This is most useful when you are designing a reusable component that encapsulates data logic while allowing the consuming parent component to customize part of its layout.
- 组件的构成一个再复杂的组件,都是由三部分组成的:prop、event、slot,它们构成了 Vue.js 组件的 API。如果你开发的是一个通用组件,那一定要事先设计好这三部分,因为组件一旦发布,后面再修改 API 就很困难了,.
In vue.js props are used to pass the data to its child components, but it is hard to pass when we have a complex code. In such cases slots can be used. Let’s create a new component called Post by adding the slot element.
- Syntax to use Vue Slot with
v-slot
directive along with its shorthand - How to use Vue Named Slots with
v-slot
& examples - How to use Vue
v-slot
for Scoped Slots & examples - Vue Dynamic slots example
Related Post: Vue 3 Composition API tutorial with examples
Contents
Vue slots syntax with v-slot directive
With new v-slot
directive, we can:
– combine html layers: component tag and scope of the slot.
– combine the slot and the scoped slot in a single directive.
For example, this is old syntax with slot-scope
:
This is how we combine ListComponent
and template
tag:
And this is old named slots syntax:
Now we use new Vue v-slot
directive:
You can see that:
– We use <template v-slot:header>
to wrap <p>
tag instead of <p slot='header'>
directly. This is because Vue v-slot
can only be used in <component>
or <template>
html tag. It cannot be used in plain HTML tags (<p>
for example).
– We replace slot='content' slot-scope='{data}'
with v-slot:content='{data}'
by combining slot
& slot-scope
. With new Vue v-slot
directive, all slots are compiled into scoped slots. It improves the performance. Why?
Normal slots are rendered during the parent component’s render cycle. So, if any dependency of a slot changes, both the parent and child components will be re-rendered.
When we use scoped slots, slots are compiled into inline functions and called during the child component’s render cycle. This means:
- data from a scoped slot are collected by the child component which is re-rendered separately.
- the changes of parent scope dependency only affect the parent, not the child component. So the child component doesn’t need to update if it uses only scoped slots.
Shorthand for v-slot
#
is the shorthand for Vue v-slot
directive.
For example, #content
stands for v-slot:content
.
The code above can be written as:
Remember that when using shorthand, we must always specify the name of the slot after #
symbol. We cannot use shorthand like this: #='{item}'
.
It must be: #default='{item}'
in which, #default
is the shorthand for v-slot:default
.
In the next parts, we show you some examples that apply new Vue v-slot
directive in practice.
Vue v-slot examples with Named Slots
If we want to use multiple slots in one component, Named Slots are useful.
The code below shows BkrCard
component template with 3 slots:
- header
- title
- default
Remember that <slot>
without name
attribute has the name default
.
Now look at the parent component which use v-slot
directive to specify name for named slots on <template>
tag:
The result will be:
If we pass only one named slot, the default value will be shown:
Vue v-slot example with default slot
In the example above, we use <template v-slot:default>
for the default slot.
We have other ways to specify html code to be considered as default slot also:
– wrap it in a <template>
without Vue v-slot
directive:
– do not wrap it in a <template>
:
The result are the same for 2 cases:
Vue v-slot examples with Scoped Slots
What we should do when we want a child component to allow parent component access its data?
In this example, categories
need to be available to the slot content in the parent. So we bind the categories
as an attribute to the <slot>
element:
The categories
attribute is called slot props.
In the parent scope, Vue v-slot
directive can help us get value of the slot props above:
The result will be:
- Dart
- Flutter
- Vue.js
This is shorthand for v-slot
:
Vue Dynamic slots example
We can use a JavaScript expression in v-slot
directive argument with square brackets:
Now look at the example:
Clicking on the Change button will change the collection
value dynamically.v-slot:[collection]='{categories}'
could become:
v-slot:default='{categories}'
v-slot:new_categories='{categories}'
This is BkrCategories
component with default
and new_categories
slot name:
The result will be:
Conclusion
We’ve learned almost aspects of new Vue v-slot
directive, from v-slot
syntax to its handshort, then apply v-slot
directive on Named Slot examples to Scoped Slots and Dynamic Slots examples.
Happy learning! See you again!
Further reading
Base Example
Cette page est en cours de traduction. Pour nous aider, vous pouvez participer sur le dépôt GitHub dédié de Vuejs-FR.
There are situations when you want the template inside the slot to be able to access data from the child component that is responsible for rendering the slot content. This is particularly useful when you need freedom in creating custom templates that use the child component’s data properties. That is a typical use case for scoped slots.
Imagine a component that configures and prepares an external API to be used in another component, but is not tightly coupled with any specific template. Such a component could then be reused in multiple places rendering different templates but using the same base object with specific API.
We’ll create a component (GoogleMapLoader.vue
) that:
- Initializes the Google Maps API
- Creates
google
andmap
objects - Exposes those objects to the parent component in which the
GoogleMapLoader
is used
Below is an example of how this can be achieved. We will analyze the code piece-by-piece and see what is actually happening in the next section.
Let’s first establish our GoogleMapLoader.vue
template:
Now, our script needs to pass some props to the component which allows us to set the Google Maps API and Map object:
This is just part of a working example, you can find the whole example in the Codesandbox below.
Real-World Example: Creating a Google Map Loader component
1. Create a component that initializes our map
GoogleMapLoader.vue
In the template, we create a container for the map which will be used to mount the Map object extracted from the Google Maps API.
Next up, our script needs to receive props from the parent component which will allow us to set the Google Map. Those props consist of:
- mapConfig: Google Maps config object
- apiKey: Our personal api key required by Google Maps
Then, we set the initial values of google and map to null:
On mounted
hook we instantiate a googleMapApi
and Map
objects from the GoogleMapsApi
and we set the values of google
and map
to the created instances:
So far, so good. With all that done, we could continue adding the other objects to the map (Markers, Polylines, etc.) and use it as an ordinary map component.
But, we want to use our GoogleMapLoader
component only as a loader that prepares the map — we don’t want to render anything on it.
To achieve that, we need to allow the parent component that will use our GoogleMapLoader
to access this.google
and this.map
that are set inside the GoogleMapLoader
component. That’s where scoped slots really shine. Scoped slots allow us to expose the properties set in a child component to the parent component. It may sound like Inception, but bear with me one more minute as we break that down further.
2. Create component that uses our initializer component.
TravelMap.vue
In the template, we render the GoogleMapLoader
component and pass props that are required to initialize the map.
Our script tag will look like this:
Still no scoped slots, so let’s add one.
3. Expose google
and map
properties to the parent component by adding a scoped slot.
Vue Js Slot Props Download
Finally, we can add a scoped slot that will do the job and allow us to access the child component props in the parent component. We do that by adding the <slot>
tag in the child component and passing the props that we want to expose (using v-bind
directive or :propName
shorthand). It does not differ from passing the props down to the child component, but doing it in the <slot>
tag will reverse the direction of data flow.
GoogleMapLoader.vue
Now, when we have the slot in the child component, we need to receive and consume the exposed props in the parent component.
Vuejs Slot Prop
4. Receive exposed props in the parent component using slot-scope
attribute.
To receive the props in the parent component, we declare a template element and use the slot-scope
attribute. This attribute has access to the object carrying all the props exposed from the child component. We can grab the whole object or we can de-structure that object and only what we need.
Let’s de-structure this thing to get what we need.
TravelMap.vue
Even though the google
and map
props do not exist in the TravelMap
scope, the component has access to them and we can use them in the template.
You might wonder why would we do things like that and what is the use of all that?
Scoped slots allow us to pass a template to the slot instead of a rendered element. It’s called a scoped
slot because it will have access to certain child component data even though the template is rendered in the parent component scope. This gives us the freedom to fill the template with custom content from the parent component.
5. Create factory components for Markers and Polylines
Now when we have our map ready we will create two factory components that will be used to add elements to the TravelMap
.
GoogleMapMarker.vue
GoogleMapLine.vue
Both of these receive google
that we use to extract the required object (Marker or Polyline) as well as map
which gives as a reference to the map on which we want to place our element.
Each component also expects an extra prop to create a corresponding element. In this case, we have marker
and path
, respectively.
On the mounted hook, we create an element (Marker/Polyline) and attach it to our map by passing the map
property to the object constructor.
There’s still one more step to go…
6. Add elements to map
Let’s use our factory components to add elements to our map. We must render the factory component and pass the google
and map
objects so data flows to the right places.
We also need to provide the data that’s required by the element itself. In our case, that’s the marker
object with the position of the marker and the path
object with Polyline coordinates.
Here we go, integrating the data points directly into the template:
We need to import the required factory components in our script and set the data that will be passed to the markers and lines:
When To Avoid This Pattern
It might be tempting to create a very complex solution based on the example, but at some point we can get to the situation where this abstraction becomes an independent part of the code living in our codebase. If we get to that point it might be worth considering extraction to an add-on.
Wrapping Up
That’s it. With all those bits and pieces created we can now re-use the GoogleMapLoader
component as a base for all our maps by passing different templates to each one of them. Imagine that you need to create another map with different Markers or just Markers without Polylines. By using the above pattern it becomes very easy as we just need to pass different content to the GoogleMapLoader
component.
This pattern is not strictly connected to Google Maps; it can be used with any library to set the base component and expose the library’s API that might be then used in the component that summoned the base component.