# [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 的證明。 ![](https://i.imgur.com/sX8rkJH.png) 所以不使用解構的話,程式碼可以這樣子看: ```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
By clicking below, you agree to our
terms of service
.
Sign in via Facebook
Sign in via Twitter
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
New to HackMD?
Sign up