Re-usable Web Interfaces with client-side Frameworks and Web Components (Part 1)

In the first part of our series we will clear up the differences between web frameworks and Web Components and motivate their common usage. Why shouldn’t we develop highly interactive applications with standardized user interfaces? To answer this question, we will elaborate fundamental problems with the examples of Angular (2+) and Polymer (1.x).

Background

In the last ten years, the technological progress made interactive web applications more necessary than ever. To reach this goal and to close the gap between needed features and existing technologies of current web standards, client-side web frameworks came up. They often provide functions which go beyond the implementation of user interfaces. For instance, Angular allows us to implement entire Single-Page Applications (SPA). Nevertheless, the main focus of client-side frameworks are of course highly interactive user interfaces.

Therefore, they usually go along with a component based application development where interfaces are divided into enclosed and reusable components. And since they are normally based upon native web technologies, their resulting solutions aren’t far away from each other.

However, from a general point of view, each framework adds an abstraction layer to provide different design patterns and architectures. For instance, Angular uses “angularized” HTML templates for its components while React offers JSX for this purpose. These differences cause incompatibilities and result in interface components that become practically unusable outside the frameworks’ bounds. If we change our web framework, we have to re-implement the majority of our interface components.

Web Components and Polymer

In contrast to the web frameworks above, Web Components are a way to implement interface components natively. The topic describes four different specifications named HTML Templates, HTML Imports, Custom Elements and Shadow DOM. Although these specifications are still under an ongoing specification process (except HTML Templates), most modern browsers have already implemented them.

However, the Web Components specifications implicate a loss of programming efficiency. Since the specifications are completely separated, the given ways of communication are limited. For instance, there isn’t a standardised data binding mechanism between Custom Elements and HTML Templates.

For this reason, frameworks like Google’s Polymer simplify the programming of Web Components. The crucial difference between Polymer and web frameworks like Angular is that Polymer still uses the native specifications. In fact, it just provides an abstraction layer to define Web Components but still uses the native technologies under the hood.

Using Angular and Polymer together

Since there are huge differences between client-side web frameworks and as mentioned above, we want to focus on Angular (2+). Also, we will use Polymer to implement Web Components. However, it isn’t the goal of this post to introduce the usage of Polymer elements within Angular applications. In fact, we want to elaborate problems and their respective backgrounds.

Problems?—A deeper view

At first glance, it seems to be obvious that Polymer shouldn’t cause crucial problems. This assumption relies on the fact that it only uses native DOM specification for its communication. Outputs of elements can be read out with events (Change events), whereas inputs can be transmitted via properties (or attributes). Since Angular can listen on events and set properties, we will focus on the communication from the Angular to the Polymer side.

Angular Rendering Architecture

For better understanding, we have to take a deeper look at the rendering architecture of Angular. A general goal of Angular is the separation of its application logic and the underlying platform (here: the browser). Between these layers, a renderer handles requests and translates them into platform specific code (here: DOM operations). In the figure below, you can see parts of Angular mapped to their corresponding layer.

application and platform logic

Besides the fact that Angular can be run in native platform (e.g. using NativeScript) as well, Angular reaches a second goal with the separation of concerns: it can and it does pre-compile its application logic. Main parts of this compilation process are the translation of “angularized” templates into runnable JavaScript code and the extraction of needed information for the change detection mechanism. This results in huge performance boosts since for instance the change detection mechanism can check for differences without comparing values using DOM operations.

Where are the problems now?

If you focus on the above figure again, you will recognize that native elements are part of the platform layer, whereas directives are located inside the application layer. This fact shouldn’t surprise you since Directives are Angular’s way to add application logic to elements referenced in its templates (e.g. for DOM elements or Angular components).

So, whenever we use Polymer elements (or Web Components) inside an Angular application, they’re native elements and only part of the platform layer. This means that elements work fine only while they’re independent from functionality provided within Directives (f. e. integration to the change detection). If the element has to be integrated in the application logic, we have to assume unexpected behaviours.

A failing use-case – the google-chart element

One example which fails without additional synchronisations between Angular and Polymer occurs if non-primitive types (f. e. objects) are bound to polymer elements’ properties. As an example we'll use Polymer’s google-chart element which receives an object containing diagram entries and internally creates charts by passing the data to the Google Chart API. Also the element encapsulates the functionality that diagrams redraw automatically after their entries have changed.

A use-case which integrates the google-chart element inside an Angular application can be seen in this Repository. The element is referenced in an Angular template and its data (containing the entries) is bound via data binding. The data itself is retrieved from the template’s corresponding component.

The element initially draws the diagram. So far so good, but what happens if our data changes afterwards? As mentioned above, the diagram should redraw, but it doesn’t!

The reason is quite clear. JavaScript objects are mutable and because of this, the reference of the data object is identical even after changes. Since we have bound the entire object (respectively its reference) to the google-chart element and Angular doesn’t hold application logic about the element, it ignores the applied change.

On the side of Polymer, we have to notify changes of mutable objects explicitly. But since we don’t do this in this example, the element doesn’t redraw. Therefore, we have to call the notification methods after applying changes which can be done within the change detection mechanism. However, since the change detection is part of the application and thus pre-compiled before runtime, we can’t simply manipulate it. In fact, we have to implement the missing logic before Angular renders its application.

Conclusion

The integration of Polymer elements works fine until we have to extend the application logic of Angular at runtime. Since each Polymer element has different behaviour, we have to add functionalities for them individually before the application renders. We have treated one case in which the usage of Angular and Polymer doesn’t work as expected. But there are several other non-trivial areas, especially when talking about forms and their validation.

If we apply the integration aspects known from Angular and Polymer to the general topic of Web Components and web frameworks, we can definitely see a regularity. Whenever frameworks add an abstraction layer to include their architecture and patterns, it results in compatibility issues with native specifications like Web Components.

Because of this, it is the responsibility of framework developers to decide whether their internal functions are reconcilable with the Web Components specifications. In my opinion, no changes will happen until the specification process has been accomplished.

In the second part of this series we will try to solve the problems mentioned. Therefore, we will focus on possible solutions to synchronise between Angular and Polymer.

comments powered by Disqus