-
Notifications
You must be signed in to change notification settings - Fork 378
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Constructor arguments in custom elements #605
Comments
I think you'll have to elaborate on the processing model. |
How does this work with the |
If you just want to provide default constructor arguments, I'd suggest you do that when defining the constructor. You could even use expressions to look them up in a DI container, e.g. |
Yeah, I don't think we want to bake a particular way of doing dependency injection into the platform. |
@treshugart Yeah I know, it's a well know fact that const originalCreateElement = HTMLDocument.prototype.createElement
HTMLDocument.prototype.createElement = ( elementName, options ) => {
const constructor = customElements.get( elementName )
if ( constructor ) {
// get arguments somewhere with a public API with a creative name
let args = customElements.getRegisteredArgumentsForConstructor( constructor )
// or from a well known symbol in the constructor?
// let args = constructor[elementDependenciesSymbol]
return new constructor(...args)
else {
return originalCreateElement.call(this, elementName, options)
}
} |
We certainly don't want to use the second argument of On the other hand, it might be that we can just expose that second argument in the constructor. That'd let authors do whatever they want to do besides dependency injection like configuring elements for one way or another. One thing to keep in mind is that there's no equivalent mechanism to do this in HTML because we only have elements and attributes in HTML, and attributes are added after the element is constructed. |
@rniwa current |
That's fine. There's no harm in the constructor to see that object in the argument. |
I too really want to see this through. As others have said, the ElementCreationOptions dictionary already exists and may be extended as the requirements change. In regards to DI-patterns, following @domenic's suggestion of providing default constructor arguments directly from the element constructor means that the Custom Element classes must depend on the service container, which, besides being a bit verbose and slightly annoying in practice, also may lead to circular dependencies (depending on the implementation) if the Custom Element itself is also injected as a service. |
I found a kinda Here is a simple sudo code of what I mean // I do DI of the dependencies here
function MyComponent(http, bar) {
return class extends HTMLElement {
constructor() {
super()
this.http = http
this.bar = bar
}
}
}
// We can use typescript decorators as well
MyComponent.parameters = [[new Inject(Http)], [new Inject(Bar)]];
const injector = ReflectiveInjector.resolveAndCreate([
Http,
Bar,
{
provide: MyComponent,
useFactory: (http, bar) => new MyComponent(http, bar),
deps: [Http, Bar]
}
]);
customElements.define('my-component', injector.get(MyComponent)); For a working version of the above code, refer https://github.com/ibhi/webcomponent-with-di/blob/master/src/users.component.js |
Let's close this. There are plentiful workarounds, and upon re-reading, the proposal seems to be "provide the UA with a no-argument function it can call to create elements even if the constructor takes mandatory arguments". However, I think it's much simpler and fits better with the platform to flip that around: make the constructor the no-argument function the platform calls, and if you want to have a mandatory-arguments factory, you can e.g. use static methods on your custom element class. |
As it wasn't said here :
let elem = new (customElements.get('my-custom') )("foo", "faa", "fuu")
class MyCustom extends HTMLElement {
constructor(foo = null) { }
}
function bindCstr(custom, ...args) {
class X extends custom {
constructor() {
super(...args)
}
}
return X;
}
customElements.define(name, bindCstr(MyCustom, 'foo') ); |
Dependency injection is a very well established pattern that greatly improves test-ability of a piece of software. Is common for components to use external code in the form of a dependency, like an API client for example which developers want to mock during tests. In a world of modules we shouldn't rely on global variables.
This dependencies could be retrieved by the browser from a user defined function that returns an array(or any iterable?) of objects or primitives that are passed as constructor arguments when the custom element is instantiated.
An alternative could be a function that allows users to instantiate elements manually.
The text was updated successfully, but these errors were encountered: