Monday, September 1, 2014

Creating Web Components (custom elements) in AngularJS 1.x

I'm continuously looking for ways to write UI code (JavaScript, HTML, CSS) that will have as long of a shelf life as possible.  This is despite the fact that web technologies and fads change every few months or so.  My main motivation is that I am lazy, as any good web developer should be.  I don't want to have to keep rewriting the same logic every few months.  A by product of being lazy, is code that is more cost effective because it can live longer.

It's a reasonably safe bet that Web Components APIs will become part of the lingua franca of the web in the near future, and JavaScript frameworks will either support them or die.  Standardization is still a ways off for some of the APIs, and due to various technical reasons most of the current polyfills cannot truly emulate the APIs reliably and completely across modern browsers, most notably, Shadow DOM. 

However, after reading TJ VanToll's blog post, it got me thinking about what can be done now in production ready, cross-browser code to help future proof components and apps.  It seems that Custom Elements can be polyfilled across all the latest browsers with two caveats.  The :unresolved pseudo element can't be used yet, and some of the method and callback names (not logic) may still change before standardization.  Both of these issues are minor and definitely not show stoppers.

Now it's not really debatable that Shadow DOM is the magical browser capability that UI component developers crave for encapsulation, especially for component CSS.  But Custom Elements provide more value than might be apparent on the surface. After all we can make up element names now and the browser won't complain, right?

Besides allowing new elements to become official citizens of the DOM rather than illegal aliens, Custom Elements allow us component developers to define the APIs that allow components to be export, imported, shared, and re-used within frameworks and across frameworks.  This is because APIs are plain old HTML element APIs- attributes, properties, methods, and events.

Lately I've been playing with W3C Custom Element generation from within AngularJS 1.x code.  When AngularJS 2.0 arrives, it will be integrated with Web Components APIs.  It will be able to import and export Custom Elements, and will have specialized directives including template and component directives to do this rather than the generic directive definitions we have now in 1.x.

Below is a GitHub Gist I posted of an AngularJS service that acts as a generic Custom Element factory, and what I would define as an AngularJS 1.x component directive that accesses it.  It works in Chrome and Opera without a polyfill, and the rest with a custom-element-only polyfill like https://github.com/WebReflection/document-register-element




This is the first version, and I have yet to play with it using existing AngularJS components, but I plan to do so extensively.  One of the big questions about creating custom element from inside a framework is where the framework should end and the DOM APIs begin.  In other words, should we now be thinking about placing most or all of the component's logic in it's DOM properties and methods, or will much of the logic still remain in the framework part of the "framework enhanced" component (i.e. $scope)?  Storing logic and data in the DOM makes custom element components independent of any framework, and therefore portable and sharable.  But then we have everyone accessing the global context!