# [Vuetify] Vuetify 元件中常看到的 `<template v-slot:activator="{on, attrs}"` 是什麼東東? ###### tags: `前端筆記` `Vuetify` `Vue` 最近專案需要頻繁地使用 [Vuetify](https://vuetifyjs.com/en/),雖然官方文件寫的很用心,也有很多範例,但是我 Vue 的知識還是太淺,看了同事的程式碼才更理解... ## ==TODO:== 2022/03/31 整理: - [7 Secret Patterns Vue Consultants Don’t Want You to Know - Chris Fritz](https://www.youtube.com/watch?v=7lpemgMhi0k&t=1314s) - [Vuetify, tooltips: what are "on" and "attrs" for?](https://stackoverflow.com/questions/62702245/vuetify-tooltips-what-are-on-and-attrs-for) ## 同事及網路上的使用範例 1. 同事大神的程式碼(使用 popup 效果做出彈跳式地日曆選擇器): ```javascript= <v-dialog ref="endDateDialogRef" v-model="endDateModal" :return-value.sync="dates.endDate" :retain-focus="false" persistent width="290px" > <template v-slot:activator="{ on, attrs }"> <v-text-field v-model="dates.endDate" label="迄日" append-icon="mdi-calendar" readonly v-bind="attrs" outlined v-on="on" /> </template> <v-date-picker v-model="dates.endDate" > <v-spacer /> <v-btn text color="primary" @click="endDateModal = false" > Cancel </v-btn> <v-btn text color="primary" @click="$refs.endDateDialogRef.save(dates.endDate)" > OK </v-btn> </v-date-picker> </v-dialog> ``` 2. 網路上找到的[範例](https://codepen.io/abdelsalam-shahlol/pen/poEYJqN),一樣是點擊後出現 popup 的日曆 比對兩個範例得知如果要有這樣子的效果,必須要使用 `<v-dialog>`、`<template v-slot:activator="...">` 包覆 `<input>` 並在關閉 `<v-dialog>` 前插入 `<v-date-picker>`。其他的 properties 暫時先不管他,因為其他的看官網文件應該沒問題,但是讓我最好奇的是:**到底 `<template v-slot:activator="...">` 是什麼東東。** ## Scoped slot(作用域插槽) 一開始我理解 slot 就認為它知能做一個像是包裝紙(wrapper)的東西,讓 parent 可以把 parent 的 template 給 child(也就是 slot),沒想到 slot 還有另一個很酷的功能,也就是標題的 scoped slot(作用域插槽)。 透過 scoped slot 讓開發者也可以把 slot 所屬的 component 有辦法將自身的 state 丟給 parent 使用(雖然語法看起來很怪...)。 假設目前我有兩個 components,App.vue(parent) 以及 PlaySlot.vue(child),在 child 中用 slot,把該 component 當作 wrapper: ```javascript= // App.vue <template> <div id="app"> <play-slot> <h1>Hello</h1> </play-slot> </div> </template> ``` ```javascript= // PlaySlot.vue <template> <div> <slot></slot> <p>In Play slot</p> </div> </template> ``` 沒什麼稀奇,因為 slot wrapper 會把在 parent 中間的 template 插進去。 ```htmlembedded= <div> <div> <h1>Hello</h1> <p>In Play slot</p> </div> </div> ``` 那如果 child 自己也有 state 想要傳給 parent 呢? `<slot name="something" :keyName="value" />` 如 parent 傳資料給 child,可用像 props 的語法 `:keyName="value"`,也可以給名字 `name=""` `<template v-slot:Name="slotProps"`,讓程式碼的閱讀性更加。 ```javascript= // PlaySlot.vue <template> <div> <slot></slot> <p>In Play slot</p> // slot 也可以把 child state 向上傳 <slot name="activator" :user="user"></slot> // === <slot name="activator" v-bind:user="user"> </div> </template> <script> // child 自己的 state export default { data() { return { user: 'Lun' } } } </script> ``` ```javascript= // App.vue <template> <div id="app"> <play-slot> <h1>Hello</h1> <template v-slot:activator="slotProps"> <h2>{{ slotPrps.user }}</h2> </template> </play-slot> </div> </template> ``` 編譯後就會得到: ```htmlembedded= <div> <div> <h1> Hello </h1> <p> In Play Slot </p> <h2> Lun </h2> </div> </div> ``` 除了把資料丟給 parent,`<slot>` 也可以從 parent 接資料後再把資料丟給 parent: ```javascript= // App.vue <div> <play-slot :number="number"> <h1>Hello</h1> <template v-slot:test="props"> // 從 parent 得到的 number 又藉由 slot 丟到 parent 了 <h2>{{ props.number }}</h2> </template> </play-slot> </div> // PlaySlot.vue <template> <div> <slot></slot> <p>{{ number }}</p> <p>In Play slot</p> <slot name="test" v-bind:number="number"></slot> </div> </template> ``` 所以 scoped slot 不僅可以丟 child 自己本身的 state 到 parent,也可以從 parent 拿到 state 後再藉由 scoped slot 丟還給 parent。 但不要忘記,有一條鐵的紀律要記住: > Everything in the parent template is compiled in parent scope; everything in the child template is compiled in the child scope. > 所有出現在 parent template 會被編譯為 parent 作用域,反之,所有出現在 child template 就回被編譯為 child 作用域。 ## Scoped slot 從 child 傳 state 到 parent 就像是 function 根據 Vue 的[官方文件](https://v2.vuejs.org/v2/guide/components-slots.html?redirect=true#Destructuring-Slot-Props),scoped slot 底層就像是定義一個單個 argument function 一樣,因此也可以使用解構(destructure) ```javascript= function (slotProps) { // ... slot content ... } ``` ```javascript= <current-user v-slot="{ user }"> {{ user.firstName }} </current-user> ``` 也可以寫 default ```javascript= <current-user v-slot="{ user = { firstName: 'Guest' } }"> {{ user.firstName }} </current-user> ``` ## 回到 Vuetify 的範例 ```javascript= <v-dialog v-model="endDateModal" ... > <template v-slot:activator="{ on, attrs }"> <v-text-field v-model="dates.endDate" v-bind="attrs" v-on="on" ... /> </template> <v-date-picker v-model="dates.endDate" > .... </v-date-picker> </v-dialog> ``` ### 1. `<template>` 是從 parent 取得 props 後再藉由 scoped slot 丟回 parent 如前面所述,這個例子的 `<template v-slot:activator="{on, attrs}">` 是一個 scoped slot,所以它其實就是把 child 本身的 state / 從 parent 拿到的 state 拿回給 parent,讓 parent template 可以取得需要的 state。 #### 要怎麼確定 `<template>` 真的有拿 parent 的 state? ```javascript= // 把 template 的解構拿掉,換成一般的樣子 <v-dialog> <template v-slot:activator="slotProps"> <v-text-field v-model="dates.endDate" v-bind="attrs" v-on="on" @click="test(slotProps)" ... /> </template> </v-dialog> <srcipt> ... test(slotProps) { console.log(slotProps) } </script> ``` 執行完確實會出現 `slotProps`,代表是 scoped slot 傳上去給 parent 的證明。  所以不使用解構的話,程式碼可以這樣子看: ```javascript= <v-dialog v-model="endDateModal" ... > // 中規中矩,不適用 destructure <template v-slot:activator="slotProps"> <v-text-field v-model="dates.endDate" v-bind="slotProps.attrs" v-on="slotProps.on" ... /> </template> <v-date-picker v-model="dates.endDate" > .... </v-date-picker> </v-dialog> ``` ### 2. `activator` 也是幕後功成之一 根據 Vuetify 的[文件](https://vuetifyjs.com/en/api/v-dialog/#events),`<v-dialog>` 的 `v-slot:activator` 會觸發 `<v-dialog>` component(藉由點擊或者其他 event),且 `<v-dialog>` 會傳事件給 `<template>`(事件被包裝成物件,且 `key` 為 `on`)。 如果開發者想要使用怎麼便利的元件,就必須乖乖使用 `V-slot:activator`。 ### 3. `v-on`、`v-bind` 也可以收物件 因為 `<template>` 接到的資料為: ```javascript= {attrs: {....}, on: {click: callback} } ``` 根據[文件](https://v2.vuejs.org/v2/api/?redirect=true#v-on),`v-on` 可以接物件(`v-on="{eventName: callbak}"`)。 ```javascript= <!-- object syntax (2.4.0+) --> <button v-on="{ mousedown: doThis, mouseup: doThat }"></button> ``` [同理](https://v2.vuejs.org/v2/api/?redirect=true#v-bind),`v-bind="attrs"` 就是接物件包裝好的 `HTML accessibility` 屬性。 ## 參考資料 1. 程式範例 - [Date Picker inside dialog - Vuetify](https://codepen.io/abdelsalam-shahlol/pen/poEYJqN) - [Vuetify Inline Datatable Editor](https://codepen.io/Jayesh_v/pen/wvgjMva) 2. 文章 - [Vuetify组件中常见的v-slot:activator=“{ on, attrs }“是什么意思?](https://blog.csdn.net/weixin_44710964/article/details/107428727) - [Meaning of v-slot:activator="{ on }"](https://stackoverflow.com/questions/55188478/meaning-of-v-slotactivator-on/55194478)
×
Sign in
Email
Password
Forgot password
or
Sign in via Google
Sign in via Facebook
Sign in via X(Twitter)
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
Continue with a different method
New to HackMD?
Sign up
By signing in, you agree to our
terms of service
.