1 | /**
|
2 | * @license
|
3 | * Copyright 2017 Google LLC
|
4 | * SPDX-License-Identifier: BSD-3-Clause
|
5 | */
|
6 | /**
|
7 | * Overview:
|
8 | *
|
9 | * This module is designed to add support for an async `setValue` API and
|
10 | * `disconnected` callback to directives with the least impact on the core
|
11 | * runtime or payload when that feature is not used.
|
12 | *
|
13 | * The strategy is to introduce a `AsyncDirective` subclass of
|
14 | * `Directive` that climbs the "parent" tree in its constructor to note which
|
15 | * branches of lit-html's "logical tree" of data structures contain such
|
16 | * directives and thus need to be crawled when a subtree is being cleared (or
|
17 | * manually disconnected) in order to run the `disconnected` callback.
|
18 | *
|
19 | * The "nodes" of the logical tree include Parts, TemplateInstances (for when a
|
20 | * TemplateResult is committed to a value of a ChildPart), and Directives; these
|
21 | * all implement a common interface called `DisconnectableChild`. Each has a
|
22 | * `_$parent` reference which is set during construction in the core code, and a
|
23 | * `_$disconnectableChildren` field which is initially undefined.
|
24 | *
|
25 | * The sparse tree created by means of the `AsyncDirective` constructor
|
26 | * crawling up the `_$parent` tree and placing a `_$disconnectableChildren` Set
|
27 | * on each parent that includes each child that contains a
|
28 | * `AsyncDirective` directly or transitively via its children. In order to
|
29 | * notify connection state changes and disconnect (or reconnect) a tree, the
|
30 | * `_$notifyConnectionChanged` API is patched onto ChildParts as a directive
|
31 | * climbs the parent tree, which is called by the core when clearing a part if
|
32 | * it exists. When called, that method iterates over the sparse tree of
|
33 | * Set<DisconnectableChildren> built up by AsyncDirectives, and calls
|
34 | * `_$notifyDirectiveConnectionChanged` on any directives that are encountered
|
35 | * in that tree, running the required callbacks.
|
36 | *
|
37 | * A given "logical tree" of lit-html data-structures might look like this:
|
38 | *
|
39 | * ChildPart(N1) _$dC=[D2,T3]
|
40 | * ._directive
|
41 | * AsyncDirective(D2)
|
42 | * ._value // user value was TemplateResult
|
43 | * TemplateInstance(T3) _$dC=[A4,A6,N10,N12]
|
44 | * ._$parts[]
|
45 | * AttributePart(A4) _$dC=[D5]
|
46 | * ._directives[]
|
47 | * AsyncDirective(D5)
|
48 | * AttributePart(A6) _$dC=[D7,D8]
|
49 | * ._directives[]
|
50 | * AsyncDirective(D7)
|
51 | * Directive(D8) _$dC=[D9]
|
52 | * ._directive
|
53 | * AsyncDirective(D9)
|
54 | * ChildPart(N10) _$dC=[D11]
|
55 | * ._directive
|
56 | * AsyncDirective(D11)
|
57 | * ._value
|
58 | * string
|
59 | * ChildPart(N12) _$dC=[D13,N14,N16]
|
60 | * ._directive
|
61 | * AsyncDirective(D13)
|
62 | * ._value // user value was iterable
|
63 | * Array<ChildPart>
|
64 | * ChildPart(N14) _$dC=[D15]
|
65 | * ._value
|
66 | * string
|
67 | * ChildPart(N16) _$dC=[D17,T18]
|
68 | * ._directive
|
69 | * AsyncDirective(D17)
|
70 | * ._value // user value was TemplateResult
|
71 | * TemplateInstance(T18) _$dC=[A19,A21,N25]
|
72 | * ._$parts[]
|
73 | * AttributePart(A19) _$dC=[D20]
|
74 | * ._directives[]
|
75 | * AsyncDirective(D20)
|
76 | * AttributePart(A21) _$dC=[22,23]
|
77 | * ._directives[]
|
78 | * AsyncDirective(D22)
|
79 | * Directive(D23) _$dC=[D24]
|
80 | * ._directive
|
81 | * AsyncDirective(D24)
|
82 | * ChildPart(N25) _$dC=[D26]
|
83 | * ._directive
|
84 | * AsyncDirective(D26)
|
85 | * ._value
|
86 | * string
|
87 | *
|
88 | * Example 1: The directive in ChildPart(N12) updates and returns `nothing`. The
|
89 | * ChildPart will _clear() itself, and so we need to disconnect the "value" of
|
90 | * the ChildPart (but not its directive). In this case, when `_clear()` calls
|
91 | * `_$notifyConnectionChanged()`, we don't iterate all of the
|
92 | * _$disconnectableChildren, rather we do a value-specific disconnection: i.e.
|
93 | * since the _value was an Array<ChildPart> (because an iterable had been
|
94 | * committed), we iterate the array of ChildParts (N14, N16) and run
|
95 | * `setConnected` on them (which does recurse down the full tree of
|
96 | * `_$disconnectableChildren` below it, and also removes N14 and N16 from N12's
|
97 | * `_$disconnectableChildren`). Once the values have been disconnected, we then
|
98 | * check whether the ChildPart(N12)'s list of `_$disconnectableChildren` is empty
|
99 | * (and would remove it from its parent TemplateInstance(T3) if so), but since
|
100 | * it would still contain its directive D13, it stays in the disconnectable
|
101 | * tree.
|
102 | *
|
103 | * Example 2: In the course of Example 1, `setConnected` will reach
|
104 | * ChildPart(N16); in this case the entire part is being disconnected, so we
|
105 | * simply iterate all of N16's `_$disconnectableChildren` (D17,T18) and
|
106 | * recursively run `setConnected` on them. Note that we only remove children
|
107 | * from `_$disconnectableChildren` for the top-level values being disconnected
|
108 | * on a clear; doing this bookkeeping lower in the tree is wasteful since it's
|
109 | * all being thrown away.
|
110 | *
|
111 | * Example 3: If the LitElement containing the entire tree above becomes
|
112 | * disconnected, it will run `childPart.setConnected()` (which calls
|
113 | * `childPart._$notifyConnectionChanged()` if it exists); in this case, we
|
114 | * recursively run `setConnected()` over the entire tree, without removing any
|
115 | * children from `_$disconnectableChildren`, since this tree is required to
|
116 | * re-connect the tree, which does the same operation, simply passing
|
117 | * `isConnected: true` down the tree, signaling which callback to run.
|
118 | */
|
119 | import { Disconnectable, Part } from './lit-html.js';
|
120 | import { Directive } from './directive.js';
|
121 | export * from './directive.js';
|
122 | /**
|
123 | * An abstract `Directive` base class whose `disconnected` method will be
|
124 | * called when the part containing the directive is cleared as a result of
|
125 | * re-rendering, or when the user calls `part.setConnected(false)` on
|
126 | * a part that was previously rendered containing the directive (as happens
|
127 | * when e.g. a LitElement disconnects from the DOM).
|
128 | *
|
129 | * If `part.setConnected(true)` is subsequently called on a
|
130 | * containing part, the directive's `reconnected` method will be called prior
|
131 | * to its next `update`/`render` callbacks. When implementing `disconnected`,
|
132 | * `reconnected` should also be implemented to be compatible with reconnection.
|
133 | *
|
134 | * Note that updates may occur while the directive is disconnected. As such,
|
135 | * directives should generally check the `this.isConnected` flag during
|
136 | * render/update to determine whether it is safe to subscribe to resources
|
137 | * that may prevent garbage collection.
|
138 | */
|
139 | export declare abstract class AsyncDirective extends Directive {
|
140 | /**
|
141 | * The connection state for this Directive.
|
142 | */
|
143 | isConnected: boolean;
|
144 | /**
|
145 | * Initialize the part with internal fields
|
146 | * @param part
|
147 | * @param parent
|
148 | * @param attributeIndex
|
149 | */
|
150 | _$initialize(part: Part, parent: Disconnectable, attributeIndex: number | undefined): void;
|
151 | /**
|
152 | * Sets the value of the directive's Part outside the normal `update`/`render`
|
153 | * lifecycle of a directive.
|
154 | *
|
155 | * This method should not be called synchronously from a directive's `update`
|
156 | * or `render`.
|
157 | *
|
158 | * @param directive The directive to update
|
159 | * @param value The value to set
|
160 | */
|
161 | setValue(value: unknown): void;
|
162 | /**
|
163 | * User callbacks for implementing logic to release any resources/subscriptions
|
164 | * that may have been retained by this directive. Since directives may also be
|
165 | * re-connected, `reconnected` should also be implemented to restore the
|
166 | * working state of the directive prior to the next render.
|
167 | */
|
168 | protected disconnected(): void;
|
169 | protected reconnected(): void;
|
170 | }
|
171 | //# sourceMappingURL=async-directive.d.ts.map |
\ | No newline at end of file |