Disclaimer: Do not use my code without considering its quite obvious short-comings! If you’d like to use a more robust implementation of a spread directive, read the last paragraph.
Context
When i was working on my time-tracker app i was confronted with repeatedly writing out attributes for a custom web component i have written. I was contemplating about how to approach this daunting repetition of code.
I couldn’t come up with an opinion quickly but i a simple intermediate workaround inspired by React sparked up in my mind.
In React one is able to just spread out attributes, or more generally props, to components by utilizing the ES6 spread operator:
I wondered if something like this is possible with LitElement too. I remembered that directives where potentially capable of doing this. So i started writing my very first directive in Lit. And quite a useful one as it turned out.
Directives
Lit has, as it feels, a very selected set of built-in directives. A lot of these will solve one or many problems one might run into while writing WebComponents with Lit.
The Lit documentation has a whole page designated towards custom directives. It’s advisable to read that closely.
Types of directives
There’s only two types of directives:
- Simple functions
- Class-based directives
Simple functions are, as it feels, very simple and limited as they have to return a renderable string (or more broadly speaking: anything that turns out to be renderable).
Class-based directives in contrast are very flexible as to how one is able to manipulate the output of the directive, which still has to be a string/renderable nonetheless. As one can hook into the lifecycle of a components with them, one can virtually do anything with them.
Writing the spread directive
Looking at the API Documentation for custom directives it’s easy to figure out, that implementing a class-based directive should not be too big of a feat.
There’s only two method one can implement (apart from the constructor):
render
renderfeels a bit like the simple function directive, it’s supposed to return something that is renderable One important fact about therendermethod is, that it serves as the signature of the directive function (even if it is not being utilized otherwise)update
updateenables you to imperatively access the DOM - this is what we need for our spread directive as we will be directly accessing properties of the component we want to spread properties on.
The following is the full code i wrote for my spread directive:
|
|
Ok, let’s make this a bit more digestible. The core to create your custom directive is the following:
That’s the bare minimum. When creating a class-based custom directive, the last
line becomes obligatory to add. Calling the directive function with our custom
directive class creates the actual custom directive that can then be exported.
Adding the call signature for our directive
Remember that the render method is used to derive the signature for our
directive. This forced me to add the render function just for that purpose.
In order to signal Lit that render actually returns nothing to render, we add
the nothing sentinel value.
update is where the magic happens
The update method is the key to the spread directive. It takes two parameters:
- A
Partobject exposing an API specific to the part - An array containing all arguments passed to the
rendermethod.
The Part type is crucial for our implementation. We want to use the
ElementPart which
is the one that can be used within element tags.
We extract the obj we are expecting out of the render argument’s array. That
obj will be iterated over and all properties on it will override the same
property on the target element.
The return value is the
noChange sentinel
value in order to indicate that we do not want to re-render anything. If a
re-render of the component the directive is acting on is necessary, it will
derive this on its own by the changes on its properties.
|
|
Look further
After implementing the spread directive, i searched the web for an existing directive. Turns out, Google wrote one themselves but did not include with the built-in ones.
The spread directive is part of the lit-helpers module which doesn’t consist of too many helpers at the time of writing this.
The spread directive in that module is much more feature rich than the simple one i wrote. It is capable of attaching properties, attributes, boolean attributes and event listeners by spreading them. The sigils are necessary for those though.