Practical use of scoped slots with GoogleMaps (EN)

Base Example

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.

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.

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

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.

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.

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.

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.