async-directive.d.ts 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  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 { directive } 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