Vue slot usage (easy to understand)

Posted by driver_x on Wed, 08 Dec 2021 00:08:12 +0100

Because in 2.6.0, named slots and scope slots introduced a new unified syntax (i.e. v-slot instruction). It replaces slot and slot scope, and now the Internet is talking about some old versions of content, and the official documents are not easy to understand, so I sorted out an article on the use of slots

The common understanding of slot is "pit occupation", which occupies a good position in the component template. When the component label is used, the content in the component label will automatically fill the pit (replace the slot position in the component template)
And can be used as an exit for hosting distribution content

Content slot
Define two components: home.vue and test.vue
Then reference the test.vue component in the home.vue component

The slot can contain plain text

//home.vue
<test>
     Hello Word
</test>

//test.vue
<a href="#">
     <slot></slot>
</a>

When the component is rendered, < slot > < / slot > will be replaced by Hello Word

The slot can also contain any template code, including HTML

By introducing the Font Awesome icon style into your index.html, you can directly use the icons in it

<link href="//netdna.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet">

//home.vue
<test>
    <!-- Add a Font Awesome Icon -->
    <span class="fa fa-user"></span>
    Hello Word
</test>

Add other components in the slot

//home.vue
<test>
    <!-- Add an icon component -->
    <font-awesome-icon></font-awesome-icon>
    Hello Word
</test>

If < test > does not contain a < slot > element, anything between the start tag and the end tag of the component will be discarded.

Using data in slots

Slots, like other parts of the template, can access the same instance properties (that is, the same "scope"), but not the scope of < test >

//home.vue
<test>
    //The slot can get the contents of the home component
    Hello {{enhavo}}
</test>

data(){
    return{
        enhavo:'word'
    }
}

//home.vue
//name cannot be obtained here, because this value is passed to < test >
<test name='you'>
    Hello {{name}}
</test>

Rules:
All contents in the parent template are compiled in the parent scope; Everything in the sub template is compiled in the sub scope.

Fallback content (default content) slot
Sometimes we need to set a specific default content for the slot. When other components do not give you content, the default content will be rendered

//test.vue
//Set the default content Submit in the slot
<button>
  <slot>Submit</slot>
</button>

stay home.vue Direct use in test.vue As follows:

//home.vue
<test></test>

Then the last set default content Submit will be rendered

<button>
   Submit
</button>

What if we provide content?

//home.vue
<test>Button</test>

Then the provided content will be rendered instead of the default content

<button>
   Button
</button>

Named slot
Sometimes we need multiple slots in a component

So what? In this case, the < slot > element has a special feature: name, which can be used to define additional slots

<div>
  <header>
    <!-- We want to put the header here -->
  </header>
  
  <main>
    <!-- We want to put the main content here -->
  </main>
  
  <footer>
    <!-- We want to put the footer here -->
  </footer>
</div>

At this time, we can use name attribute

<div>
  <header>
    <slot name="header"></slot>
  </header>
  
  <main>
    <slot></slot>
  </main>
  
  <footer>
    <slot name="footer"></slot>
  </footer>
</div>

If a < slot > does not have a name attribute, its name defaults to default
When providing content to a named slot, we can use the v-slot instruction on the < template > element and provide its name as a parameter

<div>
   <template v-slot:header>
    <h1>Here might be a page title</h1>
   </template>

  <p>A paragraph for the main content.</p>
  <p>And another one.</p>

  <template v-slot:footer>
    <p>Here some contact info</p>
  </template>
</div>

Now everything in the < template > element will be passed into the corresponding slot. Anything not wrapped in < template > with v-slot will be regarded as the content of the default slot.

If you want to be more specific, set name="default" in the slot of the main content, and then wrap the content above

<template v-slot:default>
    <p>A paragraph for the main content.</p>
    <p>And another one.</p>
</template>

Note: v-slot can only be added to one < template > (there is only one exception, which will be described below)

Scope slot
As mentioned above, slots can access the same instance properties (that is, the same "scope") as other parts of the template, but not the scope of < test >

What if you want to access the < test > scope?
We bind the content to be transferred to < slot >, and then set a value in the parent component with v-slot to define the name of the slot we provide:

//test.vue
<div>
    <!-- Set default:{{user.lastName}}obtain Jun -->
    <!-- If home.vue If the slot value is given in, it will not be displayed Jun -->
    <!-- Set a usertext Then put user Tied to the set usertext upper -->
    <slot v-bind:usertext="user">{{user.lastName}}</slot>
</div>

//Define content
data(){
  return{
    user:{
      firstName:"Fan",
      lastName:"Jun"
    }
  }
}

Then receive the transmitted value in home.vue:

//home.vue
<div>
  <test v-slot:default="slotProps">
    {{slotProps.usertext.firstName}}
  </test>
</div>

In this way, you can get the value passed by the test.vue component

The feature bound to the < slot > element is called slot prop. In the parent component, we can use v-slot to set a value to define the name of the slot prop we provide, and then use it directly

Abbreviation syntax for exclusive default slots
In the above case, when the content provided is only the default slot, the label of the component can be used as the template of the slot. In this way, we can use v-slot directly on components

In this way, the writing method can be simpler. Because the v-slot without parameters is assumed to be the default slot, the above code can also be simplified:

<div>
  <!-- Can put :default Removed, default slot only -->
  <test v-slot="slotProps">
    {{slotProps.usertext.firstName}}
  </test>
</div>

Note: the abbreviation syntax of the default slot cannot be mixed with the named slot because it will lead to ambiguous scope

<div>
  <!-- Can put :default Removed, default slot only -->
  <test v-slot="slotProps">
    {{slotProps.usertext.firstName}}
    <!-- Invalid, warning -->
    <template v-slot:other="otherSlotProps">
      slotProps is NOT available here
    </template>
  </test>
</div>

As long as multiple slots appear, always use the complete < template > based syntax for all slots:

<test>
  <template v-slot:default="slotProps">
    {{ slotProps.user.firstName }}
  </template>

  <template v-slot:other="otherSlotProps">
    ...
  </template>
</test>

Deconstruct slot Prop
Because the internal working principle of the scope slot is to include the contents of your slot in a function that passes in a single parameter
This means that the value of v-slot can actually be any JS expression that can be used as a parameter in the function definition

So it was written like this:

<div>
  <test v-slot="slotProps">
    {{slotProps.usertext.firstName}}
  </test>
</div>

You can also write:

<div>
  <test v-slot={usertext}>
    {{usertext.firstName}}
  </test>
</div>

This makes the template more concise, especially when multiple props are provided in the slot. It also opens other possibilities such as prop renaming,

For example, you can rename usertext to person:

<div>
  <test v-slot={usertext:person}>
    {{person.firstName}}
  </test>
</div>

You can even define backup content (default content), which is used when the slot has no value:

<div>
  <test v-slot="{usertext={firstName:'Yang'}}">
    {{usertext.firstName}}
  </test>
</div>

Dynamic slot name (new in 2.6.0)
Dynamic instruction parameters (you need to know) can also be used on v-slot to define dynamic slot names:

<base-layout>
  <template v-slot:[dynamicSlotName]>
    ...
  </template>
</base-layout>

Abbreviation for named slot (new in 2.6.0)
Like v-on and v-bind, v-slot also has abbreviations, that is, replace all the contents before the parameter (v-slot:) with characters #. For example, v-slot:header can be rewritten as #header:

It was written like this:

<div>
   <template v-slot:header>
    <h1>Here might be a page title</h1>
   </template>

  <p>A paragraph for the main content.</p>
  <p>And another one.</p>

  <template v-slot:footer>
    <p>Here some contact info</p>
  </template>
</div>  

It can now be written as follows:

<div>
   <template #header>
    <h1>Here might be a page title</h1>
   </template>

  <p>A paragraph for the main content.</p>
  <p>And another one.</p>

  <template #footer>
    <p>Here some contact info</p>
  </template>
</div>

Note: like other instructions, this instruction is only available when it has parameters

The following writing is wrong:

<test #="{ usertext }">
  {{ usertext.firstName }}
</test>

If you want to use abbreviations, you must always replace them with explicit slot names:

<test #default="{ usertext }">
  {{ usertext.firstName }}
</test>

Other examples
Slot prop allows us to convert slots into reusable templates, which can render different contents based on the input prop. This is most useful when designing reusable components that encapsulate data logic while allowing parent components to customize part layout.

For example, we want to implement a < todo list > component, which is a list and contains layout and filtering logic:

<ul>
  <li
    v-for="todo in filteredTodos"
    v-bind:key="todo.id"
  >
    {{ todo.text }}
  </li>
</ul>

We can use each todo as the slot of the parent component to control it through the parent component, and then bind todo as a slot prop:

<ul>
  <li
    v-for="todo in filteredTodos"
    v-bind:key="todo.id"
  >
    <!--
    We are for everyone todo A slot is prepared,
    take `todo` Object as a slot prop Incoming.
    -->
    <slot name="todo" v-bind:todo="todo">
      <!-- Backup content -->
      {{ todo.text }}
    </slot>
  </li>
</ul>

Now, when we use the < todo list > component, we can choose to define a different < template > for todo as an alternative, and we can obtain data from the sub component:

<todo-list v-bind:todos="todos">
  <template v-slot:todo="{ todo }">
    <span v-if="todo.isComplete">✓</span>
    {{ todo.text }}
  </template>
</todo-list>

As for the abandoned slot and slot scope features, they will not be described here. If you are interested, please refer to the official documents

Topics: Vue.js