DOMInjector providers do not apply for early attributeChangedCallback calls
Describe the bug DOMInjector configured providers are not loaded for the outmost (App)Element.
To Reproduce Reproduction alongside #1273 in my "not absolutely minimal" joist-(hono-)vite test repo... Steps to reproduce the behavior:
- Checkout and install https://github.com/Phoscur/joist-vite
- Run npm start and open localhost:4200
- See (the suppressed) logs in the browser console: ConsoleDebug should log messages for AppElement
Only adding explicit use: ConsoleDebug provider on top of AppElement will work
Expected behavior
Initialising DOMInjector before customElements should apply it for all Elements
Desktop (please complete the following information):
- OS: Windows
- Browser Chromium
- Version 120
Additional context Added logger.type to debug Debug https://github.com/Phoscur/joist-vite/commit/6465058dc4847cdcd0ac16a9890be014d5923df8
quick question. if you example. if you remove the logger from attribute changed. does it work? because attribute changed can be called before the element is actually connected.
quick question. if you example. if you remove the logger from attribute changed. does it work? because attribute changed can be called before the element is actually connected.
oh yes, you mean using
// instead of
const logger = this.#logger();
// shortcut
const logger = console;
https://github.com/Phoscur/joist-vite/blob/6465058dc4847cdcd0ac16a9890be014d5923df8/src/app/app.element.tsx#L71
and suddenly it's the right logger type!
Yes I've also seen attributeChangedCallback called before connectedCallback. Seems that explains it!
@deebloo an idea for a solution:
attributeChangedCallback(name = 'lang', oldValue: string, newValue: string) {
if (name !== 'lang' || !newValue) {
return;
}
// default inject is not ready:
// const logger = this.#logger();
// instead awaited inject:
this.#logger.ready().then((logger) => { // or similar, or const logger = await this.#logger().ready();
// get other dependencies
const i18n = this.#i18n();
.... actual code ....
});
what if the dependency is already async? Do I then need to await it twice...?
something like that could potentially help. What I currently do is track the current state using the @injected() lifecycle and don't run attributeChanged unless it has been connected. we COULD change the default behavior of attributeChanged for decorated injectables but that doesn't feel like a good solution.
What if we there was another utility function rather than just the lifecycle callbacks.
async attributeChangedCallback() {
await injectorReady(this);
// do the thing
}
Keep it as simple as possible? ;)
Yes why not make it a utility function, don't know how big the usecase is. Maybe that reads better, or makes it more prominent. Especially if it's just one dependency it would be nice to have it built into context too though:
const logger = await this.#logger.domInjectorReady();
// or even
const logger = await this.#logger(true);
It might also be a documentation thing because we already have lifecycle callbacks for when the injector itself is ready. its just that attributeChangedCallback runs before any of that happens.