- From: /#!/JoePea <trusktr@gmail.com>
- Date: Sat, 20 Aug 2016 19:13:29 -0700
- To: public-webapps WG <public-webapps@w3.org>
- Message-ID: <CAKU1PAU0nPgADSdzsWUrKs4KXezXo-VD4_f190BMEnr8M7wh5g@mail.gmail.com>
Due to the renaming of some class methods (attached/detached to connected/disconnected) and removal of createdCallback in favor of constructors (which is a good thing!), I find myself writing my WebComponent base class (class-factory mixin) as follows. My question is, should I be doing what I'm doing, or do you recommend something else? Note that the attached/detachedCallback methods simply call the connected/disconnectedCallback methods, and also note what I've done with `createdCallback` and the `constructor` due to the differences between v0 and v1. Subclasses of my WebComponent base class still use `createdCallback` rather than a constructor in order to be backwards compatible. The code is as follows, with parts removed for brevity in order to show just the parts dealing with the v0/v1 APIs directly and how backwards compatibility is maintained: // Very very stupid hack needed for Safari in order for us to be able to extend // the HTMLElement class. See: // https://github.com/google/traceur-compiler/issues/1709 if (typeof window.HTMLElement != 'function') { const _HTMLElement = function HTMLElement(){} _HTMLElement.prototype = window.HTMLElement.prototype window.HTMLElement = _HTMLElement } // XXX: Maybe we can improve by clearing items after X amount of time? const classCache = new Map function hasHTMLElementPrototype(constructor) { if (!constructor) return false if (constructor === HTMLElement) return true else return hasHTMLElementPrototype(constructor.prototype) } /** * Creates a WebComponent base class dynamically, depending on which * HTMLElement class you want it to extend from. Extend from WebComponent when * making a new Custom Element class. * * @example * import WebComponent from './WebComponent' * class AwesomeButton extends WebComponent(HTMLButtonElement) { ... } * * @param {Function} elementClass The class that the generated WebComponent * base class will extend from. */ export default function WebComponentMixin(elementClass) { if (!elementClass) elementClass = HTMLElement if (!hasHTMLElementPrototype(elementClass)) { throw new TypeError( 'The argument to WebComponentMixin must be a constructor that extends from or is HTMLElement.' ) } // if a base class that extends the given `elementClass` has already been // created, return it. if (classCache.has(elementClass)) return classCache.get(elementClass) // otherwise, create it. class WebComponent extends elementClass { // constructor() is used in v1 Custom Elements instead of // createdCallback() as in v0. constructor() { super() // If the following is true, then we know the user should be using // `document.registerElement()` to define an element from this class. // `document.registerElement()` creates a new constructor, so if the // constructor here is being called then that means the user is not // instantiating a DOM HTMLElement as expected because it is required // that the constructor returned from `document.registerElement` be used // instead (this is a flaw of Custom Elements v0 which is fixed in v1 // where class constructors can be used directly). if (document.registerElement && !customElements.define) { // TODO: link to docs. throw new Error(` You cannot instantiate this class directly without first registering it with \`document.registerElement(...)\`. See an example at http://.... `) } // Throw an error if no Custom Elements API exists. if (!document.registerElement && !customElements.define) { // TODO: link to docs. throw new Error(` Your browser does not support the Custom Elements API. You'll need to install a polyfill. See how at http://.... `) } // otherwise the V1 API exists, so call the createdCallback, which // is what Custom Elements v0 would call by default. Subclasses of // WebComponent should put instantiation logic in createdCallback // instead of in a custom constructor if backwards compatibility is // to be maintained. this.createdCallback() } createdCallback() { // code removed for brevity... } connectedCallback() { // code removed for brevity... } attachedCallback() { this.connectedCallback() } // back-compat disconnectedCallback() { // code removed for brevity... } detachedCallback() { this.disconnectedCallback() } // back-compat } classCache.set(elementClass, WebComponent) return WebComponent } Any thoughts? */#!/*JoePea
Received on Sunday, 21 August 2016 02:14:42 UTC