Use v-slot in Vuetify

Introduction

Vue has introduced a new unified syntax (namely v-slot instruction) for named slots and scoped slots since version 2.6.0. It replaces slot and slot-scope, two attributes that are currently obsolete but not removed and are still in the documentation.
In short, the main purpose of the introduction of slot technology is to assume the role of a "placeholder". I believe many friends are familiar with the concept of "placeholder", that is, when constructing the UI or implementing a certain function without giving a specific implementation plan, first use a simplest content to replace it, so as to avoid grammatical errors in the process of compilation. Or try to avoid embarrassment. Please refer to the following brief examples provided by the official:

Sometimes it is useful to set a specific fallback (that is, the default) content for a slot, it will only be rendered when no content is provided. For example, in a <submit-button> component:


<button type="submit">
  <slot></slot>
</button>

We may want to render the text "Submit" in most cases in this <button>. In order to use "Submit" as a backup content, we can put it in the <slot> tag:

<button type="submit">
  <slot>Submit</slot>
</button>

Now when I use <submit-button> in a parent component and do not provide any slot content:


<submit-button></submit-button>

The fallback content "Submit" will be rendered:

<button type="submit">
  Submit
</button>

But if we provide content:


<submit-button>
  Save
</submit-button>

The provided content will be rendered to replace the backup content:

<button type="submit">
  Save
</button>

Named slot

When providing content to a named slot, we can use the v-slot instruction on a <template> element and provide its name as a parameter of the v-slot:

<base-layout>
  <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's some contact info</p>
  </template>
</base-layout>

The header and footer after the colon above are equivalent to the name attribute of the slot after version 2.6.

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

However, if you want to be more specific, you can still wrap the contents of the default slot in a <template>:

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

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

  <template v-slot:footer>
    <p>Here's some contact info</p>
  </template>
</base-layout>

[Note] v-slot can only be added to <template> (there is only one exception), which is different from the deprecated slot attribute.

As a rule, remember:

All content in the parent template is compiled in the parent scope; all content in the child template is compiled in the child scope.

Scope slot

Sometimes it is useful to allow the contents of the slot to access data that is only available in the subcomponent.

The attribute bound to the <slot> element is called [slot prop]. Now in the parent scope, we can use [v-slot with value] to define the name of the slot prop we provide:

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

In this example, we chose to name the object containing all slot props slotProps , but you can use any name you like.

Abbreviated syntax for exclusive default slot

In the above case, when only the default slot is provided, the component label can be used as a template for the slot. This way we can use v-slot directly on the component:


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

This way of writing can be simpler. Just like assuming that the unspecified content corresponds to the default slot, v-slot without parameters is assumed to correspond to the default slot:

<current-user v-slot="slotProps">
  {{ slotProps.user.firstName }}
</current-user>

Note that the abbreviated syntax of the default slot cannot be mixed with the named slot, because it will cause the scope to be ambiguous.

Whenever multiple slots appear, always use the full <template>-based syntax for all slots:


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

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

Deconstruct Slot Prop

The internal working principle of the scoped slot is to wrap the contents of the slot in a function with a single parameter:

function (slotProps) {
  // 插槽内容
}

This means: The value of v-slot can actually be any JavaScript expression that can be used as a parameter in a function definition. So in a supported environment ( single file component or modern browser ), you can also use ES2015 destructuring to pass in specific slot props , as follows:

<current-user v-slot="{ user }">
  {{ user.firstName }}
</current-user>

This can make the template more concise, especially when the slot provides multiple props. It also opens up other possibilities such as prop renaming , such as renaming user to person:

<current-user v-slot="{ user: person }">
  {{ person.firstName }}
</current-user>

You can even define fallback content, which is used when the slot prop is undefined:

<current-user v-slot="{ user = { firstName: 'Guest' } }">
  {{ user.firstName }}
</current-user>

Slot props allow us to convert slots into reusable templates, which can render different content based on the input prop. This is most useful when designing reusable components that encapsulate data logic while allowing parent components to customize partial layouts.

In the following content, we will discuss how to use v-slot in the Vuetify environment in conjunction with the use of the common component v-menu of the Vuetify UI library.

Use v-slot in Vuetify

Please look at the code (for more complex examples, see the next two for basic examples):

<template>
  <div class="text-center">
    <v-menu>
      <template v-slot:activator="{ on: menu, attrs }">
        <v-tooltip bottom>
          <template v-slot:activator="{ on: tooltip }">
            <v-btn
              color="primary"
              dark
              v-bind="attrs"
              v-on="{ ...tooltip, ...menu }"
            >
              Dropdown w/ Tooltip
            </v-btn>
          </template>
          <span>Im A ToolTip</span>
        </v-tooltip>
      </template>
      <v-list>
        <v-list-item
          v-for="(item, index) in items"
          :key="index"
        >
          <v-list-item-title>{{ item.title }}</v-list-item-title>
        </v-list-item>
      </v-list>
    </v-menu>
  </div>
</template>

I won’t give a specific explanation now. Let’s take a look at a Q&A explanation on Codenong Island. Although it is in English, there is no problem in understanding it, so I will not translate it here:

problem:

Looking at the Vuetify example code for v-toolbar, what is the purpose of v-slot:activator="{ on }"? For example:

<template v-slot:activator="{ on }">
  <v-toolbar-title v-on="on">
    <span>All</span>
    <v-icon dark>arrow_drop_down</v-icon>
  </v-toolbar-title>
</template>

Reply

You're likely referring to this example:


<v-toolbar color="grey darken-1" dark>
  <v-menu :nudge-width="100">
    <template v-slot:activator="{ on }">
      <v-toolbar-title v-on="on">
        <span>All</span>
        <v-icon dark>arrow_drop_down</v-icon>
      </v-toolbar-title>
    </template>

    ...
  </v-menu>
</v-toolbar>

The following line declares a scoped slot named activator, and it is provided a scope object (from VMenu), which contains a property named on:

&lt;template v-slot:activator="{ on }"&gt;
This uses destructuring syntax on the scope object, which IE does not support.

For IE, you'd have to dereference on from the scope object itself:


<template v-slot:activator="scope">
  <v-toolbar-title v-on="scope.on">

But the ideal solution IMO is to use a Vue CLI generated project, which includes a Babel preset (@vue/babel-preset-app) to automatically include the transforms/polyfills needed for the target browsers. In this case, babel-plugin-transform-es2015-destructuring would be automatically applied during the build.

Details on the activator slot
VMenu allows users to specify a slotted template named activator, containing component(s) that activate/open the menu upon certain events (e.g., click). VMenu provides listeners for those events via an object, passed to the activator slot:

<v-menu>
  <template v-slot:activator="scopeDataFromVMenu">
    <!-- slot content goes here -->
  </template>
</v-menu>

The slot content can access VMenu's event listeners like this:


<v-menu>
  <template v-slot:activator="scopeDataFromVMenu">
    <button v-on="scopeDataFromVMenu.on">Click</button>
  </template>
</v-menu>

For improved readability, the scoped data can also be destructured in the template:


<!-- equivalent to above -->
<v-menu>
  <template v-slot:activator="{ on }">
    <button v-on="on">Click</button>
  </template>
</v-menu>

The listeners from the scope object are passed to the <button> with v-on's object syntax, which binds one or more event/listener pairs to the element. For this value of on:

{
click: activatorClickHandler // activatorClickHandler is an internal VMenu mixin
}
...the button's click handler is bound to a VMenu method.

To sum up, the basis of using v-slot in several commonly used components (v-menu, v-tooltip and v-dialog) in Vuetify is based on the named scope slot technology in Vue, and uses ES2015 (ie ES6) syntax "Destructing" in (destructing).

Taking the typical application of v-menu as an example, it passes its own event set and attribute set to the internal sub-components of <template> through the named scope slot, and uses v-bind and v-on syntax to determine the specific sub-components as needed. Attributes and events are mapped to the attribute set and event set corresponding to the outer parent component.

Application of v-slot combined with v-tooltip

    <v-tooltip bottom>
      <template v-slot:activator="{ on, attrs }">
        <v-btn
          color="primary"
          dark
          v-bind="attrs"
          v-on="on"
        >
          Button
        </v-btn>
      </template>
      <span>Tooltip</span>
    </v-tooltip>

v-slot combined with v-dialog application

<v-dialog
      v-model="dialog"
      width="500"
    >
      <template v-slot:activator="{ on, attrs }">
        <v-btn
          color="red lighten-2"
          dark
          v-bind="attrs"
          v-on="on"
        >
          Click Me
        </v-btn>
      </template>

      <v-card>
        <v-card-title class="headline grey lighten-2">
          Privacy Policy
        </v-card-title>

        <v-card-text>
          Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
        </v-card-text>

        <v-divider></v-divider>

        <v-card-actions>
          <v-spacer></v-spacer>
          <v-btn
            color="primary"
            text
            @click="dialog = false"
          >
            I accept
          </v-btn>
        </v-card-actions>
      </v-card>
    </v-dialog>

Important reference

https://blog.csdn.net/weixin_44710964/article/details/107428727
https://cn.vuejs.org/v2/guide/components-slots.html
https://vuetifyjs.com/en/components/menus/#activator-and-tooltip
https://www.manongdao.com/article-1850182.html

Guess you like

Origin blog.51cto.com/zhuxianzhong/2548252