- 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