(String: {%- set hs_blog_post_body -%} {%- set in_blog_post_body = true -%} <span id="hs_cos_wrapper_post_body" class="hs_cos_wrapper hs_cos_wrapper_meta_field hs_cos_wrapper_type_rich_text" style="" data-hs-cos-general-type="meta_field" data-hs-cos-type="rich_text"> <div class="blog-post__lead h2"> <p><span style="background-color: transparent; color: #515151;">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.&nbsp;</span></p> </div></span>)

Practical Use of Vue.js Scoped Slots

Photo of Mateusz Rybczonek

Mateusz Rybczonek

Updated Jan 10, 2023 • 16 min read
main-picture-1

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.

For the purpose of this blog post, I’ve created a component (GoogleMapLoader.vue) that initialises Google Maps API, creates a google and map objects and exposes those objects to the parent component in which the GoogleMapLoader is used.

See below a basic example of how this can be achieved. We will analyse the code piece by piece and see what is actually happening in the next section.

The template part will look as below

GoogleMapLoader.vue

google map loader component template tag

Our script will look like this, we'll have some props passed to the component which allows us to set the Google Maps API and Map object:

google map loader component script tag

This is just part of a working example, you can find the whole example in this Codesandbox example. We will analyse the code piece by piece and see what is actually happening in the next section.

Real-World Example: Creating a Google Map Loader component

1. Create a component that initialises our map

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.

GoogleMapLoader.vue

GML - template - 2

Inside the script part:

  • we receive props from the parent component which will allow us to set the Google Map:
    • mapConfig - Google Maps config object
    • apiKey - our personal api key required by Google Maps

GoogleMapLoader.vue

GML - script - 2a

  • we set the initial values of google and map to null

GML - script - 2b

  • on mounted hook we create an instance of GoogleMapsApi and Map object from the GoogleMapApi and we set the values of google and map to the created instances

GML - script - 2c

So far so good, with that done we could continue adding the other objects to the map (Markers, Polylines, etc.) and using it as a ordinary map component. But we want to use our GoogleMapLoader component only as a loader that prepares the map, not renders anything on it.

To achieve that we need to allow 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 an inception, but bear with me one more minute.

2. Create component that uses our initialiser component

In the template, we render the GoogleMapLoader component and pass props that are required to initialise the map.

TravelMap.vue

GML - template - 2a

Our script tag will look like below at this stage.

GML - script - 3a

Still no scoped slots, let's add one.

3. Expose google and map properties to the parent component by adding a scoped slot

So 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 reverse the direction of data flow.

GoogleMapLoader.vue

GML - template - 3a-1

Now when we have the slot in the child component we need to receive and consume the exposed props in the parent component.

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 slot-scope attribute. The attribute has access to the object carrying all props exposed from the child component. We can grab the whole object or we can destructure that object and get the required props. Let's proceed with the second option.

TravelMap.vue

GML - template - 4a-1

After doing that even though the google and map props does not exist in the TravelMap scope, the component has access to them and we can use them in the template.

Yeah, ok, but why would I do things like that, what is the use of all that?

Scoped slots allow us to pass a template to the slot instead of passing a rendered element. It’s called a “scoped” slot because although the template is rendered in the parent component scope, it will have access to certain child component data. That gives us a 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

GML - script - 5a

GoogleMapLine.vue

GML - script - 6a

  • both of them receive google from which we extract required object (Marker or Polyline) and map which gives as a reference to the map on which we want to place our element
  • each component expects also an extra prop to create a corresponding element
  • on mounted hook we create an element (Marker / Polyline) and attach it to our map by passing the map property to the object constructor

6. Add elements to map

Now let's use our factory components to add elements to our map.

To add elements to our map we render the factory component and pass the google and map objects.

We also need to provide data required by the element itself (in our case marker object with position of the marker and path object with polyline coordinates).

TravelMap.vue

GML - template - 6a

In our script we need to import the required factory components and set data that will be passed to the markers and lines.

GML - script - 7a

Summary

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 then be used in the component that summoned the base component.

It might be tempting to create a very complex solutions 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.

Photo of Mateusz Rybczonek

More posts by this author

Mateusz Rybczonek

Even though Mateusz comes from an entirely different business environment (he worked at sea for 10...
How to build products fast?  We've just answered the question in our Digital Acceleration Editorial  Sign up to get access

We're Netguru!

At Netguru we specialize in designing, building, shipping and scaling beautiful, usable products with blazing-fast efficiency
Let's talk business!

Trusted by: