published in 'JuptrTech von Juptr' on Juptr.io

0
0

Full Stack Engineer, Software enthusiarchitect, Open Source programmer, CTO@Juptr


Rüdiger Möller

Practice Report: Polymer.js and a java backend.

Part two of a series of our journey building juptr.io:
Our experiences building a front end with google's Polymer.js 

Part one can be found here:

 

Why Polymer ?


There are numerous alternatives when choosing a framework to build web front ends some of most popular probably being

  • knockoutjs - too old and not-so-great performance. Though I liked the minimalistic approach. Its easy to learn and quite powerful.
  • angularjs - 2.x - too beta, Angular 1.x too old and discontinued. Anyway in a recent Angular project I had the impression the reward of Angular 1.x did not justify the price of added complexity.
  • reactjs - seems like a great framework on a first glance, but
    a) I dislike the idea of transpiling, it adds another source of entropy to your software. In addition it limits choices in tooling and backend technology, degrades debugging and increases overall turnaround time.
    b) I dislike mixing app code and markup react-style
    c) we don't want to use JavaScript at server side (and also no typescript because of (a) )

So we decided to go for Polymer, which comes with a lot of material design prebuilt components, a simple and mostly transparent basic concept for componentization (webcomponents), and a nice polyfill for html-imports which gives a lot of automated optimization opportunities (see below).

You can check out the app I am talking about here.

The good parts ..

 


  • Its easy to get started with Polymer
  • A rich set of prebuilt components, most of them in a mature state
  • Structuring the application and re-use of components worked really well for the most part
  • High level of control (fine grained control over events and listening) with Polymer data binding
  • Good performance
  • High productivity as component library grows. One can achieve a lot with a few lines of code
  • Explicit pseudo static dependency mechanism with html imports
  • It integrates well with pure js libs
  • It plays well with the browser environment. Mixing in complex DOM manipulation such as required in our WYSIWYG article editor was really smooth
  • Browser compatibility: no problems with modern browser (chrome, firefox, edge)

Features we really missed ..


1) scoping / binding to parent scope data

Currently one can only bind to properties of a component. If one needs to bind to parent scope properties, state must be passed in as a component property. While this seems inline with the idea of component encapsulation, in practice this can get annoying when nesting application level components as a lot of state has to get passed with each nesting level. 

2) passing objects by reference

When passing data to components using data binding, all data objects get 'serialized'. So when trying to workaround (1) using 'context' objects, a lot of data is serialized and deserialized for each component boundary. This can slow down performance significantly to the point ugly hacks are needed (e.g. when rendering a nested tree recursively with large arrays like in a tree-style-discussion forum).

3) richer binding expression syntax

Currently only '!' is allowed as an operator inside a binding declaration like [[getValue(x)]] or [[!getValue(x)]]. This probably is by intent, however it just would be nice in practice if one could use simple expressions like [[(a+b==0)='1':getSomeString()]]. Currently functions are required to implement even simple expressions.

The not-so-good parts ..


Actually there was only one (rather annoying) issue with Polymer ..

 

timings ..

Timings are the bane of Polymer data binding, basically destroying testability. An app seems to work flawlessly in development and passes all test cases, however once you deploy it and use it with different network latency / bandwidth, strange errors caused by modified binding evaluation order pop up. Sometimes 'sleeping' errors are brought to life by loading resources in a different order and so on. This is caused by the way Polymer bindings work.

(Wrong) Example:

<dom-module id="my-component">
  <template>
    <div>This is [[getOne(one)]] or [[getTwo(two)]] or [[getBoth(one,two)]]</div>
  </template>

  <script>
    Polymer({
      is: 'my-component',
      properties: {
        one: { type: Object, notify: true },
        two: { type: Object, notify: true },
      },
      getOne: function(one) {
        //TIMING DEPENDENT ERROR, this.two may hold old data
        return one && ! this.two ? "ONE" : "";
      },
      getTwo: function(two) {
        //TIMING DEPENDENT ERROR (isOneSet() accesses property)
        return !this.isOneSet() && two;
      },
      getBoth: function(one,two) {
        return (two && one) ? "TWO" : "";
      },
      isOneSet: function() {
        return !!this.one;
      }
    });
  </script>
</dom-module>

this component is used like:

<my-component one="Yes" two="[[someOtherVar]]"></my-component>

This might work on your testsystem 100% as intended, however its wrong. Depending on timing, the 'getOne' method might get triggered before "two"'s value is set. As 'getOne' does not depend on 'two', the condition will not be re-evaluated once two is set so 'one' will have a state based on the previous value of "two".

NEVER access a property of a component using 'this' inside a polymer component function

The conclusion is, that one can NEVER access a property of a component using 'this' ('this.one') inside a components method, this also applies to helper functions as they might get called/triggered by bindings indirectly. One has to pass all values used inside a function's body as arguments.

Correct version:

<dom-module id="my-component">
  <template>
    <div>This is [[getOne(one,two)]] or [[getTwo(one,two)]] or [[getBoth(one,two)]]</div>
  </template>

  <script>
    Polymer({
      is: 'my-component',
      properties: {
        one: { type: Object, notify: true },
        two: { type: Object, notify: true },
      },
      getOne: function(one,two) {
        return one && ! two ? "ONE" : "";
      },
      getTwo: function(one,two) {
        return !this.isOneSet(one) && two;
      },
      getBoth: function(one,two) {
        return (two && one) ? "TWO" : "";
      },
      isOneSet: function(one) {
        return !!one;
      }
    });
  </script>
</dom-module>

HTMLImports


Html Imports enable static dependency analysis of webapplications. Its possible to automatically package and optimize webapplications either statically or dynamically in the webserver on first invocation (as we do).

Once the first request to index.html is received by the kontrakor-http WebServer (which actually wraps Undertow for core http functionality), application packaging is performed (once per start).

 

starting from index.html we recursively 

  • inline css and font imports
  • inline html imports 
  • inline and minify <script> tags as well as javascript.js files

Note: this is similar to polymers 'vulcanize' tool, however we do it at the webserver level (instead of build script) and get some extra control over packaging (e.g. support  dynamic coffeescript transpilation).
During development we test and debug with packaging turned off.

  • Simplest possible web app build pipeline: None
  • No need to keep a min.js version of every third party lib
  • splitting code into separate files does not have an extra-request penalty
  • no file clutter and 'dist' directories

...



Published in: