Skip to content

Commit

Permalink
feat(useTemplateRefsList): new function (vitest-dev#645)
Browse files Browse the repository at this point in the history
Co-authored-by: aaronz <aaron.bjym1011@outlook.com>
Co-authored-by: Anthony Fu <anthonyfu117@hotmail.com>
  • Loading branch information
3 people committed Aug 9, 2021
1 parent 5bbea75 commit 57e4c8d
Show file tree
Hide file tree
Showing 8 changed files with 199 additions and 1 deletion.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ Collection of essential Vue Composition Utilities
<a href="https://www.npmjs.com/package/@vueuse/core" target="__blank"><img src="https://img.shields.io/npm/v/@vueuse/core?color=a1b858&label=" alt="NPM version"></a>
<a href="https://www.npmjs.com/package/@vueuse/core" target="__blank"><img alt="NPM Downloads" src="https://img.shields.io/npm/dm/@vueuse/core?color=50a36f&label="></a>
<a href="https://vueuse.org" target="__blank"><img src="https://img.shields.io/static/v1?label=&message=docs%20%26%20demos&color=1e8a7a" alt="Docs & Demos"></a>
<img alt="Function Count" src="https://img.shields.io/badge/-131%20functions-13708a">
<img alt="Function Count" src="https://img.shields.io/badge/-132%20functions-13708a">
<br>
<a href="https://github.com/vueuse/vueuse" target="__blank"><img alt="GitHub stars" src="https://img.shields.io/github/stars/vueuse/vueuse?style=social"></a>
</p>
Expand Down
7 changes: 7 additions & 0 deletions indexes.json
Original file line number Diff line number Diff line change
Expand Up @@ -837,6 +837,13 @@
"category": "Sensors",
"description": "reactive swipe detection based on [`TouchEvents`](https://developer.mozilla.org/en-US/docs/Web/API/TouchEvent)"
},
{
"name": "useTemplateRefsList",
"package": "core",
"docs": "https://vueuse.org/core/useTemplateRefsList/",
"category": "Component",
"description": "shorthand for binding refs to template elements and components inside `v-for`"
},
{
"name": "useTimeAgo",
"package": "core",
Expand Down
1 change: 1 addition & 0 deletions packages/core/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ export * from './useShare'
export * from './useSpeechRecognition'
export * from './useStorage'
export * from './useSwipe'
export * from './useTemplateRefsList'
export * from './useTimeAgo'
export * from './useTimestamp'
export * from './useTitle'
Expand Down
29 changes: 29 additions & 0 deletions packages/core/useTemplateRefsList/demo.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<template>
<span v-for="i of count" :key="i" :ref="refs.set" class="mr-2">
{{ i }}
</span>
<br>
<button @click="count += 1">
Inc
</button>
<button :disabled="count <= 0" @click="count -= 1">
Dec
</button>
<note>Open the console to see the output</note>
</template>

<script setup lang="ts">
import { ref, watch, nextTick } from 'vue'
import { useTemplateRefsList } from '@vueuse/core'
const count = ref(5)
const refs = useTemplateRefsList<HTMLDivElement>()
watch(refs, async() => {
await nextTick()
console.log([...refs.value])
}, {
deep: true,
flush: 'post',
})
</script>
38 changes: 38 additions & 0 deletions packages/core/useTemplateRefsList/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
---
category: Component
---

# useTemplateRefsList

Shorthand for binding refs to template elements and components inside `v-for`.

> This function only works for Vue 3.x.
## Usage

```html
<template>
<div v-for="i of 5" :key="i" :ref="refs.set"></div>
</template>

<script setup lang="ts">
import { onUpdated } from 'vue'
import { useTemplateRefsList } from '@vueuse/core'
const refs = useTemplateRefsList<HTMLDivElement>()
onUpdated(() => {
console.log(refs)
})
</script>
```

<!--FOOTER_STARTS-->


## Source

[Source](https://github.com/vueuse/vueuse/blob/main/packages/core/useTemplateRefsList/index.ts)[Demo](https://github.com/vueuse/vueuse/blob/main/packages/core/useTemplateRefsList/demo.vue)[Docs](https://github.com/vueuse/vueuse/blob/main/packages/core/useTemplateRefsList/index.md)


<!--FOOTER_ENDS-->
105 changes: 105 additions & 0 deletions packages/core/useTemplateRefsList/index.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import { defineComponent, h, isVue2, nextTick, ref, toRefs } from 'vue-demi'
import { mount } from '../../.test'
import { useTemplateRefsList } from '.'

if (isVue2) {
// Vue 2 is not supported
it('stub', () => {})
}
else {
const Component1 = defineComponent({
setup() {
const list = ref([1, 2, 3])
const refs = useTemplateRefsList()
return { list, refs }
},
render() {
return h(
'div',
this.list.map(item => h('div', {
ref: this.refs.set,
id: `div${item}`,
})),
)
},
})

interface ChildAPI {
foo: () => string
}

const Child = defineComponent({
props: {
id: {
type: Number,
required: true,
},
},
setup(props) {
const { id } = toRefs(props)
const foo = () => {
return `foo${id.value}`
}
return { foo }
},
render() {
return h('div')
},
})

const testCom2 = defineComponent({
setup() {
const list = ref([1, 2, 3])
const refs = useTemplateRefsList<ChildAPI>()
return { list, refs }
},
render() {
return h(
'div',
this.list.map(item => h(Child, {
ref: this.refs.set,
id: item,
})),
)
},
})

describe('useTemplateRefsList', () => {
it('should be defined', () => {
expect(useTemplateRefsList).toBeDefined()
})

it('ref all 3 divs', () => {
const vm = mount(Component1)

expect(vm.refs).toBeDefined()
expect(vm.refs.length).toBe(3)
expect(vm.refs[0]).toBe(vm.$el.querySelector('#div1'))
expect(vm.refs[1]).toBe(vm.$el.querySelector('#div2'))
expect(vm.refs[2]).toBe(vm.$el.querySelector('#div3'))
})

it('v-for source update', async() => {
const vm = mount(Component1)

vm.list = [1, 2, 3, 4]
await nextTick()

expect(vm.refs.length).toBe(4)
expect(vm.refs[3]).toBe(vm.$el.querySelector('#div4'))

vm.list = [1]
await nextTick()

expect(vm.refs.length).toBe(1)
})

it('call child component methods', async() => {
const vm = mount(testCom2)

expect(vm.refs[0].foo()).toBe('foo1')
expect(vm.refs[1].foo()).toBe('foo2')
expect(vm.refs[2].foo()).toBe('foo3')
})
})
}
17 changes: 17 additions & 0 deletions packages/core/useTemplateRefsList/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { onBeforeUpdate, Ref, ref } from 'vue-demi'

export type TemplateRefsList<T> = T[] & {
set(el: Object | null): void
}

export function useTemplateRefsList<T = Element>(): Readonly<Ref<Readonly<TemplateRefsList<T>>>> {
const refs = ref<unknown>([]) as Ref<TemplateRefsList<T>>
refs.value.set = (el: Object | null) => {
if (el)
refs.value.push(el as T)
}
onBeforeUpdate(() => {
refs.value.length = 0
})
return refs
}
1 change: 1 addition & 0 deletions packages/functions.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
- [`tryOnMounted`](https://vueuse.org/shared/tryOnMounted/) — safe `onMounted`
- [`tryOnUnmounted`](https://vueuse.org/shared/tryOnUnmounted/) — safe `onUnmounted`
- [`unrefElement`](https://vueuse.org/core/unrefElement/) — unref for dom element
- [`useTemplateRefsList`](https://vueuse.org/core/useTemplateRefsList/) — shorthand for binding refs to template elements and components inside `v-for`
- [`useVModel`](https://vueuse.org/core/useVModel/) — shorthand for v-model binding
- [`useVModels`](https://vueuse.org/core/useVModels/) — shorthand for props v-model binding

Expand Down

0 comments on commit 57e4c8d

Please sign in to comment.