This PR fixes issues with several of the decorator implementations in `framework` and adds unit tests to specify and validate their functionality.
This module is a simple implementation of hierarchical dependency injection, inspired by Angular, based on this talk by Polymer/Lit engineer Justin Fagnani, and adapted from this StackBlitz experiment.
Design Goals
- Enable dependency inversion by declaring dependencies as abstract interfaces which can be swapped out with different concrete implementations without necessitating downstream code changes
- Enable bottom-up injection of dependencies (i.e., descendants can inject dependencies provided by ancestors)
- Enable top-down querying for dependencies (i.e., ancestors can query for dependencies provided by descendants)
- Enable these features through water-tight, intuitive abstractions
Core Concepts
- Abstract dependencies are represented by a "token," which serves as a runtime representation of a dependency described by a TypeScript interface
- Dependency tokens are "provided" by custom elements annotated with a
providedecorator - Dependencies can be "injected" by descendants of a provider (using the
injectdecorator to annotate a custom element property) or "queried" by ancestors of a provider (using thequeryProvidersdecorator)
Implementation
These features are implemented through an exchange of CustomEvent
extensions.
-
Providers dispatch a
DependencyProvisionevent just before theirconnectedCallbackmethod is invoked, and aProviderRemovalevent just afterdisconnectedCallback. -
Elements with
queryProviders-decorated properties listen forDependencyProvisionandProviderRemovalevents, updating the decorated property's array accordingly. -
Elements with
inject-decorated properties dispatch anInjectionRequestevent just before theirconnectedCallbackmethod is invoked. Providers listen for this event. If they provide a token matching the one specified by theinjectdecorator, the provider will:- Mutate the event's
detailproperty to insert the providedvalue stopImmediatePropagationof the event to prevent it from bubbling further up the DOM tree
Because JavaScript event bubbling and event listener invocations happen synchronously, the injector can check the
detailproperty of the dispatched event immediately after dispatching it to retrieve the injected value. This value is then assigned to a field returned by the decorated property'sgetaccessor. - Mutate the event's