Currently, it's written in specs that:
If this CustomElementRegistry contains an entry with name _name_, then return a new promise resolved with undefined.
Using .whenDefined('ce-name') is the platform mechanism to await CE dependencies without needing to rely on these being available on the global context.
Use cases for using whenDefined are pretty clear and simple:
While the component inclusion does not usually require the direct usage of the CE class, the extending part is verbose for no reason.
customElements.whenDefined('base-ce').then(
() => {
const BaseCE = customElements.get('base-ce');
customElements.define('reach-ce', class extends BaseCE {
});
}
);
Not only extremely simple to polyfill, resolving the class, which can never be garbage collected, once defined through the Registry, seems to follow the _least semantic surprise_, so that extending base components would look instead as such:
customElements.whenDefined('base-ce').then(
BaseCE => {
customElements.define('reach-ce', class extends BaseCE {
});
}
);
A promise that resolves undefined seems to have no benefit over a promise that passes along what the developer was actually waiting for, so I hope this change will be welcomed:
If this CustomElementRegistry contains an entry with name _name_, then return a new promise resolved with such entry.
Thanks for consideration.
const {whenDefined} = customElements;
Object.defineProperty(
customElements,
'whenDefined',
{
value(name) {
return whenDefined.call(this, name)
.then(() => this.get(name));
}
}
);
customElements.whenDefined('base-ce').then(
(BaseCE = customElements.get('base-ce')) => {
customElements.define('reach-ce', class extends BaseCE {
});
}
);
This seems like a reasonable request to me.
Although... customElements.define takes an argument ([Edit] meant to say options), and it's feasible that we end up adding more options, in which case we may want to pass along something like a CustomElementDefinition instead so that's something to consider to make it future proof.
@rniwa thanks for considering this improvement, although define takes at least two arguments and the third one is already the options one you're referring to, but the name is unique/global in an case.
However, if the idea is to have support for eventually scoped registries, whenDefined(name, registry = self.customElements).then(() => registry.get(name)) seems to be reasonable as well to me.
@WebReflection Since registry.whenDefined(name) is already an instance method, having to pass the this parameter twice makes no sense.
@ExE-Boss I don’t think I understand your comment, but the idea here is that ‘whenDefined’ resolves with what developers wait to be defined.
No double instance to pass at all.
@rniwa thanks for considering this improvement, although
definetakes at least two arguments and the third one is already the options one you're referring to, but the name is unique/global in an case.
Yeah, sorry, I meant to say options. What I mean is that it's possible we may want to add more states & information to a custom element definition in which case having some kind of CustomElementDefinition object which has the elements class as one of its properties might be useful instead of just giving the class itself. For now, it could just be a dictionary too.
Just a thought but perhaps that's an overkill.
@rniwa except for WebKit, all other browsers support already the options argument:
customElements.define(name, Class, options);
Even if currently used for builtin extends only, I think the define method itself is already future proof, and moving options one parameter before won't bring much benefits, but likely break backward compatibility (i.e. CE polyfills from 2014)
I can see, for namespaced/scoped/non-conflicting namespaces, the third argument with a registry property might be already a good/forward-compatible move (beside the name) so, for the time being, all whenDefined needs to do is really just resolve with anything that's being "_awaited"_, so that no extra customElements.get(name) is needed once whenDefined(name) is resolved.
The eventual future proof version of this could be whenDefined(name, options) which would be symmetric (or better, easy to reason about) with define(name, Class, options), so that if the eventual registry property is available, there won't be much confusion.
That being said, the registry eventually needs much more discussions around "_how an element can be part of a registry intead of the global shared one_", so it might be overkill indeed, as I don't see it coming any time soon, while using whenDefined is already a pretty common use.
@rniwa except for WebKit, all other browsers support already the options argument:
customElements.define(name, Class, options);Even if currently used for builtin extends only, I think the
definemethod itself is already future proof, and moving options one parameter before won't bring much benefits, but likely break backward compatibility (i.e. CE polyfills from 2014)
I think you misunderstood me. What I'm talking about is what we use to resolve the promise. I'm saying that instead of:
customElements.whenDefined('some-element'),then((someElement) => ... class OtherElement extends someElement...)
maybe we could do this:
customElements.whenDefined('some-element'),then((elementDefinition) => ... class OtherElement extends ElementDefinition.class...)
This way if a custom element's definition gets more states (e.g. it would have a different parser behavior, such a thing could be accessed from this newly added definition object. But it's probably an overkill.
@rniwa I see, but unless it’s symmetric with get(name) I don’t see it as an improvement, specially 'cause class cannot even be destructured without an alias, so that it won’t bring much better ergonomics, imho
@rniwa I see, but unless it’s symmetric with
get(name)I don’t see it as an improvement, specially 'causeclasscannot even be de destructured without an alias, so that it won’t bring much better ergonomics, imho
Fair enough. I think the next step is for someone write a PR request to HTML with WPT tests.
I think the next step is for someone write a PR request to HTML with WPT tests.
I can't work much these days due lack of proper internet connection ... so, if nobody will do that by next week, I'll put together a PR next Monday or later.
@rniwa @WebReflection What is a good way to get implementers feedback on this?
@rniwa @WebReflection What is a good way to get implementers feedback on this?
FWIW, I've implemented this behavior in WebKit as of https://trac.webkit.org/r266142.