UNPKG

7.01 kBMarkdownView Raw
1# Wire Service
2
3This is the implementation of Lightning Web Component's wire service. It enables declarative binding of services to a LWC component using the `@wire` decorator. It fulfills the goals of the [data service proposal](https://github.com/salesforce/lwc-rfcs/blob/master/text/0000-data-service.md).
4
5## Summary
6
7The `@wire` decorator provides LWC components with a declarative mechanism to express their data requirements. Imperative access (eg for DML) is not part of the wire service. A summary of the wire service follows:
8
9- Loading data is expressed declaratively by a component using `@wire([adapterId], [adapterConfig])`
10 - `[adapterId]` refers to the identity of a wire adapter.
11 - `[adapterConfig]` is an optional parameter, of type object, that defines wire adapter-specific configuration.
12 - `[adapterConfig]` may contain static values or reactive references.
13 - A reactive reference is identified with a `$` prefix. The remainder of the string identifies a class property. A change to a referenced class property causes new data to be requested from the wire adapter.
14- The wire service delegates to `wire adapters` to source, manage, and provision data. The wire service sits between wire adapters and LWC components.
15- It's all data from the wire service's perspective. Nothing is metadata.
16- It is assumed all data mutates over time yet a given snapshot of data is immutable.
17- A component receiving data does not own that data. It is comparable to a component receiving props does not own the props.
18
19## Example Use Of `@wire`
20
21Consider a component that wants to display the details of a todo item. It uses `@wire` to declare its data requirements.
22
23```js
24import { LightningElement, api, wire } from 'lwc';
25
26// the wire adapter identifier
27import { getTodo } from 'todo-api';
28
29export default class TodoViewer extends LightningElement {
30 @api id;
31
32 // declare the need for data. $id creates a reactive property tied to this.id.
33 // data is provisioned into this.todo.
34 @wire(getTodo, { id: '$id' })
35 todo;
36}
37```
38
39```html
40<template>
41 <template if:true="{todo}">
42 <input type="checkbox" checked="{todo.completed}" /> {todo.title}
43 </template>
44</template>
45```
46
47## Wire Adapter Specification
48
49The following is a summary of the [wire adapter RFC](https://github.com/salesforce/lwc-rfcs/blob/master/text/0103-wire-adapters.md).
50
51A `wire adapter` provisions data to a wired property or method using an [Event Target](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget). A factory function is registered for declarative `@wire` use by a component.
52
53```ts
54// The identifier for the wire adapter
55type WireAdapterId = Function|Symbol;
56
57// The factory function invoked for each @wire in a component. The WireEventTarget
58// allows the wire adapter instance to receive configuration data and provision
59// new values.
60type WireAdapterFactory = (eventTarget: WireEventTarget) => void;
61
62// Event the wire adapter dispatches to provision values to the wired property or method
63interface ValueChangedEvent {
64 value: any;
65 new(value: any) : ValueChangedEvent;
66}
67
68// Event types the wire adapter may listen for
69type EventType = 'config' | 'connect' | 'disconnect';
70
71// Event listener callback
72type Listener = (config?: { [key: string]: any ) => void;
73
74// Target of the @wire
75interface WireEventTarget extends EventTarget {
76 dispatchEvent(event: ValueChangedEvent): boolean;
77 addEventListener(type: EventType, listener: Listener): void;
78 removeEventListener(type: EventType, listener: Listener): void;
79}
80```
81
82In the component's `wiring` lifecycle, the wire service invokes the `wireAdapterFactory` function to configure an instance of the wire adapter for each `@wire` instance (which is per component instance).
83
84`eventTarget` is an implementation of [Event Target](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget) that supports listeners for the following events:
85
86- `config` is delivered when the resolved configuration changes. A singular argument is provided: the resolved configuration.
87- `connect` is delivered when the component is connected.
88- `disconnect` is delivered when the component is disconnected.
89
90The wire service remains responsible for resolving the configuration object. `eventTarget` delivers a `config` event when the resolved configuration changes. The value of the configuration is specific to the wire adapter. The wire adapter must treat the object as immutable.
91
92The wire adapter is responsible for provisioning values by dispatching a `ValueChangedEvent` to the event target. `ValueChangedEvent`'s constructor accepts a single argument: the value to provision. There is no limitation to the shape or contents of the value to provision. The event target handles property assignment or method invocation based on the target of the `@wire`.
93
94The wire adapter is responsible for maintaining any context it requires. For example, tracking the values it provisions and the originating resolved configuration is shown in the basic example below.
95
96### Registering A Wire Adapter
97
98A wire adapter must be registered with the wire service. The `wire-service` module provides a registration function.
99
100```ts
101register(adapterId: WireAdapterId, wireAdapterFactory: WireAdapterFactory);
102```
103
104## Example Wire Adapter Implementation
105
106```js
107import { register, ValueChangedEvent } from 'wire-service';
108
109// Imperative access.
110export function getTodo(config) {
111 return getObservable(config).map(makeReadOnlyMembrane).toPromise();
112}
113
114// Declarative access: register a wire adapter factory for @wire(getTodo).
115register(getTodo, function getTodoWireAdapterFactory(eventTarget) {
116 let subscription;
117 let config;
118
119 // Invoked when config is updated.
120 eventTarget.addListener('config', (newConfig) => {
121 // Capture config for use during subscription.
122 config = newConfig;
123 });
124
125 // Invoked when component connected.
126 eventTarget.addListener('connect', () => {
127 // Subscribe to stream.
128 subscription = getObservable(config)
129 .map(makeReadOnlyMembrane)
130 .subscribe({
131 next: (data) =>
132 wiredEventTarget.dispatchEvent(
133 new ValueChangedEvent({ data, error: undefined })
134 ),
135 error: (error) =>
136 wiredEventTarget.dispatchEvent(
137 new ValueChangedEvent({ data: undefined, error })
138 ),
139 });
140 });
141
142 // Invoked when component disconnected.
143 eventTarget.addListener('disconnect', () => {
144 // Release all resources.
145 subscription.unsubscribe();
146 });
147});
148```
149
150# Contributing To The Wire Service
151
152## Build & run the playground
153
154A _playground_ of LWC components using @wire is included. They're served from a basic node server and accessible in your browser.
155
156```bash
157yarn start
158```
159
160Load the examples in a browser:
161
162```bash
163open http://localhost:3000/
164```