Web Components in Angular, Ember, and React.
This is Part 3 of a 3-part series on Web Components.
- Part 1: The Specs
- Part 2: Practical Use (Browser Support and Other Challenges)
- Part 3: Web Components in Angular, Ember, and React
Each of the popular front-end frameworks has a well-defined way to build a UI component. Ember has components, Angular has directives, and React is pretty much nothing but components.
So why would you want to put a Web Component in an app built with one of these frameworks? Web Components let you create elements that apps can interface with in the same way that they already interface with native elements. That means that a Web Component can be reused anywhere, in any framework.
That’s the theory, at least.
Let’s try it out. We’ll just be looking at Custom Elements, the heart of Web Components. Here’s a simple Custom Element that takes in a single value, uppercases it, and prints out “Hello, VALUE.” We’re also including the document-register-element.js polyfill.
Custom Element Round 1 – JS BinRound One: The Basics
Here is that same component in each framework, used to display items from a list:
Ember
Ember Round 1 – JS BinReact
React Round 1 – JS BinAngular
Angular Round 1 – JS BinEmber and React are looking great (though it seems that you can only use data-
attributes with React right now. A custom attribute didn’t work). Unfortunately, Angular has some issues. Here’s what’s happening:
The element’s created callback is fired three times – once for the element that’s initially in the page (which gets wiped out shortly thereafter), and twice more for the actual elements repeated in the list. However, those two elements’ HTML is re-inserted into the DOM with the curly braces still in the attribute: {{item.value}}
. Angular later updates them with the proper values, but it’s too late for our created callback, which has already inserted {{ITEM.VALUE}}
into the body of the element. And when Angular parses that, of course there’s nothing to match the now-uppercase variable, so it becomes empty: “Hello, .”
Interestingly, if you move your Angular app JS above your HTML, you get a different failing result, where the created callback seems to occur after Angular’s parsing has already taken place, and the curly braces remain in the body of the Custom Elements: “Hello, {{ITEM.VALUE}}.” However, you still get the missing/empty version in polyfilled browsers or if you add an empty script tag at the end of the body (which seems to cause Angular to parse again). Anyhow, it’s weird.
If the Custom Element wasn’t modifying the value before inserting it – it might appear to be working fine in Angular. But it would really only have passed the template text through – not the actual value.
Angular is clearly off to a bad start with Custom Elements, thanks to its HTML-based templates. Ember and React store their templates elsewhere, and are thus immune to the issues Angular has.
Round Two: Binding
Let’s make our Custom Element a bit more complicated by allowing it to update its content based on changes to the attribute.
Custom Element Round 2 – JS BinHere we go – be sure to click the “Change Values” buttons:
Ember
Ember Round 2 – JS BinReact
React Round 2 – JS BinAngular
Angular Round 2 – JS BinGreat, that part worked in all three frameworks! Unfortunately, Angular’s initial render is still coming out blank (though I had hoped this would fix it) – but only in non-polyfilled browsers (like Chrome). Here’s what’s happening:
- The Custom Element gets created with the template attribute, and inserts
{{ITEM.VALUE}}
into its body. - Angular updates the attribute with the proper value. The element correctly reinserts this new value into its body.
- Angular then blanks out the value, despite the fact that there weren’t any curly braces in the element’s body anymore. I don’t know exactly why this is happening, but I’m guessing that Angular records the presense of the curly braces after the created callback but before the attributeChanged callback. Something like that.
- Any additional changes of the attribute value work fine.
- In polyfilled browsers – the callbacks occur later in Angular’s initialization process than they normally would. So the attributeChanged callback fires after Angular’s template parsing is done, and it works.
Round 3: More Binding
Now what if we want to pass a changed value in a Custom Element back up into the app (two-way binding)? I’ve added a button to our Custom Element that updates its attribute with a few random characters:
Custom Element Round 3 – JS BinUnfortunately, none of the frameworks support this out-of-the-box. I couldn’t find anything to enable it either Ember or React (if you know of something, please let me know!).
There is an Angular directive that can make this work; it references Polymer, which is too bad because it works fine for plain Custom Elements. The only caveat is that it doesn’t seem like you can use data-
attributes (since Angular automatically de-data’s them).
So that’s cool!
However, as you can see, I’ve inherited another problem. In the process of adding the “update” button, I switched to appending in my created callback. And the Custom Element gets “created” once before Angular re-injects the HTML string, causing it to get “created” a second time. I could easily solve this by emptying out the element when its created – but it’s kind of silly that I should have to.
Conclusion
Depending on what you need out of your Custom Elements, you may be able to use them terrifically in Ember or React. Angular, not so much. I’d love to see future releases of each framework provide first-class support for Custom Elements. I don’t know for sure if or when that will happen, but for some possible hints, here are a few comments from each framework:
Ember
Once Custom Elements are widely available in browsers, you should be able to easily migrate your Ember components to the W3C standard and have them be usable by other frameworks.
This is so important to us that we are working closely with the standards bodies to ensure our implementation of components matches the roadmap of the web platform. (source)
React
I definitely think it’s the wrong programming paradigm. I really hope that [web components] do not succeed. (source)
Angular
[Angular 2 will have] Support for Web Components out of the box. Many assumptions we made about the web platform in 1.x are no longer valid, and Angular will change to accommodate these. (10/2014).
Angular 2 builds on new web standards like ES6 and Web Components. This means that Angular 2 works seamlessly with the libraries also built on these standards, including Polymer, X-Tag, and others. (3/2015).