Skip to content

Commit e33bdc8

Browse files
NataliaTepluhinantepluhinabencodezenznck
authored
Provide/inject (#48)
* Fixed an example * feat: added component-slots * fix: fixed config * Update src/guide/component-slots.md Co-Authored-By: Ben Hong <ben@bencodezen.io> * Update src/guide/component-slots.md Co-Authored-By: Ben Hong <ben@bencodezen.io> * Update src/guide/component-slots.md Co-Authored-By: Ben Hong <ben@bencodezen.io> * Update src/guide/component-slots.md Co-Authored-By: Ben Hong <ben@bencodezen.io> * Update src/guide/component-slots.md Co-Authored-By: Ben Hong <ben@bencodezen.io> * fix: fixed default slot wrapping * fix: fixed compilation scope name * feat: described provide-inject basics * feat: started reactive provide/inject * fix: added provide-inject illustration * fix: changed the name to dependency provider * fix: fixed examples * Update src/guide/component-provide-inject.md Co-Authored-By: Rahul Kadyan <hi@znck.me> * Update src/guide/component-provide-inject.md Co-Authored-By: Rahul Kadyan <hi@znck.me> * Update src/guide/component-provide-inject.md Co-Authored-By: Rahul Kadyan <hi@znck.me> * fix: fixed vuepress config Co-authored-by: ntepluhina <ntepluhina@gitlab.com> Co-authored-by: Ben Hong <ben@bencodezen.io> Co-authored-by: Rahul Kadyan <hi@znck.me>
1 parent 9c5d269 commit e33bdc8

File tree

4 files changed

+119
-49
lines changed

4 files changed

+119
-49
lines changed

src/.vuepress/config.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ const sidebar = {
2424
'/guide/component-registration',
2525
'/guide/component-props',
2626
'/guide/component-custom-events',
27-
'/guide/component-slots'
27+
'/guide/component-slots',
28+
'/guide/component-provide-inject'
2829
]
2930
}
3031
]
Loading

src/guide/component-provide-inject.md

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
# Provide / inject
2+
3+
> This page assumes you've already read the [Components Basics](components.md). Read that first if you are new to components.
4+
5+
Usually, when we need to pass data from the parent to child component, we use [props](component-props.md). Imagine the structure where you have some deeply nested components and you only need something from the parent component in the deep nested child. In this case, you still need to pass the prop down the whole component chain which might be annoying.
6+
7+
For such cases, we can use the `provide` and `inject` pair. Parent components can serve as dependency provider for all its children, regardless how deep the component hierarchy is. This feature works on two parts: parent component has a `provide` option to provide data and child component has an `inject` option to start using this data.
8+
9+
![Provide/inject scheme](/images/components_provide.png)
10+
11+
For example, if we have a hierarchy like this:
12+
13+
```
14+
Root
15+
└─ TodoList
16+
├─ TodoItem
17+
└─ TodoListFooter
18+
├─ ClearTodosButton
19+
└─ TodoListStatistics
20+
```
21+
22+
If we want to pass the length of todo-items directly to `TodoListStatistics`, we would pass the prop down the hierarchy: `TodoList` -> `TodoListFooter` -> `TodoListStatistics`. With provide/inject approach, we can do this directly:
23+
24+
```js
25+
const app = Vue.createApp({})
26+
27+
app.component('todo-list', {
28+
data() {
29+
return {
30+
todos: ['Feed a cat', 'Buy tickets']
31+
}
32+
},
33+
provide: {
34+
user: 'John Doe'
35+
},
36+
template: `
37+
<div>
38+
{{ todos.length }}
39+
<!-- rest of the template -->
40+
</div>
41+
`
42+
})
43+
44+
app.component('todo-list-statistics', {
45+
inject: ['foo'],
46+
created() {
47+
console.log(`Injected property: ${this.user}`) // > Injected property: John Doe
48+
}
49+
})
50+
```
51+
52+
However, this won't work if we try to provide some Vue instance property here:
53+
54+
```js
55+
app.component('todo-list', {
56+
data() {
57+
return {
58+
todos: ['Feed a cat', 'Buy tickets']
59+
}
60+
},
61+
provide: {
62+
todoLength: this.todos.length // this will result in error 'Cannot read property 'length' of undefined`
63+
},
64+
template: `
65+
...
66+
`
67+
})
68+
```
69+
70+
To access Vue instance properties, we need to convert `provide` to be a function returning an object
71+
72+
```js
73+
app.component('todo-list', {
74+
data() {
75+
return {
76+
todos: ['Feed a cat', 'Buy tickets']
77+
}
78+
},
79+
provide() {
80+
return {
81+
todoLength: this.todos.length
82+
}
83+
},
84+
template: `
85+
...
86+
`
87+
})
88+
```
89+
90+
This allows us to more safely keep developing that component, without fear that we might change/remove something that a child component is relying on. The interface between these components remains clearly defined, just as with props.
91+
92+
In fact, you can think of dependency injection as sort of “long-range props”, except:
93+
94+
- parent components don’t need to know which descendants use the properties it provides
95+
- child components don’t need to know where injected properties are coming from
96+
97+
## Working with reactivity
98+
99+
In the example above, if we change the list of `todos`, this change won't be reflected in the injected `todoLength` property. This is because `provide/inject` bindings are _not_ reactive by default. We can change this behavior by passing a `ref` property or `reactive` object to `provide`. In our case, if we want to react to changes in the ancestor component, we need to assign a Composition API `computed` property to our provided `todoLength`:
100+
101+
```js
102+
app.component('todo-list', {
103+
// ...
104+
provide() {
105+
return {
106+
todoLength: Vue.computed(() => this.todos.length)
107+
}
108+
}
109+
})
110+
```
111+
112+
In this, any change to `todos.length` will be reflected correctly in the components, where `todoLength` is injected. Read more about `reactive` provide/inject in the [Composition API section](TODO)

src/guide/component-slots.md

Lines changed: 5 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ To provide content to named slots, we need to use the `v-slot` directive on a `<
195195
<template v-slot:default>
196196
<p>A paragraph for the main content.</p>
197197
<p>And another one.</p>
198-
<template v-slot:default>
198+
</template>
199199

200200
<template v-slot:footer>
201201
<p>Here's some contact info</p>
@@ -364,9 +364,10 @@ This can make the template much cleaner, especially when the slot provides many
364364
You can even define fallbacks, to be used in case a slot prop is undefined:
365365

366366
```html
367-
<current-user v-slot="{ user = { firstName: 'Guest' } }">
368-
{{ user.firstName }}
369-
</current-user>
367+
<todo-list v-slot="{ item = 'Placeholder' }">
368+
<i class="fas fa-check"></i>
369+
<span class="green">{{ todo }}<span>
370+
</todo-list>
370371
```
371372

372373
## Dynamic Slot Names
@@ -418,47 +419,3 @@ Instead, you must always specify the name of the slot if you wish to use the sho
418419
<span class="green">{{ item }}<span>
419420
</todo-list>
420421
```
421-
422-
## Other Examples
423-
424-
**Slot props allow us to turn slots into reusable templates that can render different content based on input props.** This is most useful when you are designing a reusable component that encapsulates data logic while allowing the consuming parent component to customize part of its layout.
425-
426-
For example, we are implementing a `<todo-list>` component that contains the layout and filtering logic for a list:
427-
428-
```html
429-
<ul>
430-
<li v-for="todo in filteredTodos" v-bind:key="todo.id">
431-
{{ todo.text }}
432-
</li>
433-
</ul>
434-
```
435-
436-
Instead of hard-coding the content for each todo, we can let the parent component take control by making every todo a slot, then binding `todo` as a slot prop:
437-
438-
```html
439-
<ul>
440-
<li v-for="todo in filteredTodos" v-bind:key="todo.id">
441-
<!--
442-
We have a slot for each todo, passing it the
443-
`todo` object as a slot prop.
444-
-->
445-
<slot name="todo" v-bind:todo="todo">
446-
<!-- Fallback content -->
447-
{{ todo.text }}
448-
</slot>
449-
</li>
450-
</ul>
451-
```
452-
453-
Now when we use the `<todo-list>` component, we can optionally define an alternative `<template>` for todo items, but with access to data from the child:
454-
455-
```html
456-
<todo-list v-bind:todos="todos">
457-
<template v-slot:todo="{ todo }">
458-
<span v-if="todo.isComplete">✓</span>
459-
{{ todo.text }}
460-
</template>
461-
</todo-list>
462-
```
463-
464-
However, even this barely scratches the surface of what scoped slots are capable of. For real-life, powerful examples of scoped slot usage, we recommend browsing libraries such as [Vue Virtual Scroller](https://github.com/Akryum/vue-virtual-scroller) or [Vue Promised](https://github.com/posva/vue-promised)

0 commit comments

Comments
 (0)