Thursday, December 22, 2016

Passing "this" Around In Polymer

Lately, I have decided to write the front ends for my personal Web projects in Polymer.  It gives me the ability to construct the UI, and even tie in AJAX actions and database calls, by simply including Web Components as elements in the DOM, just like plain HTML.  To me, it seems less bloated and denser than even Angular 1 (sorry, haven't played with Angular 2 yet), not to mention plain JavaScript or JQuery where you still need to write out most of the interactions between the model, view, controller, and external APIs yourself.  The Web Components aspect was most appealing to me because now I could leverage previous work, standing on the shoulders of giants, rather than reinventing the wheel for the needed interactions.  Better yet, if I use Google's implementations of Web components, I can even get Material styling on my DOM elements with no extra work.

However, Polymer suffers from some of the problems I've had with Perl too.  You really need to be in the right frame of mind when writing in Polymer because the syntax is deceptively short and you need to be intimately aware of what the framework is handling for you.  This could make it tougher to explain what's going on in the code if haven't looked at it in a while.  "Oh yes, you can build this in Polymer, and it looks just like HTML!" I once touted to a crowd, but then upon closer inspection -- no -- there were a lot of other idiosyncrasies specific to Polymer they would need to know before they could go off on their own and replicate my work!  Finally, while Perl is generally easy to find help for, it can be frustrating in the world of Polymer because many answers pertain to older versions of the framework,

Despite these pitfalls, many people who love front-end website design and are adept with XML-based layouts have been flocking to use Polymer for building fully-functional Web apps.  In fact, now you don't even need to write JavaScript to do 3D scenery in a browser, so if you enjoy using A-Frame for building 3D scenery and you need to build an entire Web app, then you would probably enjoy Polymer as well.  For me, it's about doing more with less, and Polymer seems to minimize the amount of code I need to write, not to mention the amount of technology I need to get involved -- the days of having to build an API on your server to talk to the remote API might be coming to an end!

One of the biggest things I've busted my head on in Polymer thus far was working with the this variable.  You use this to access the functions and properties defined within the scope of Polymer.  This is OK when you have a diverse set of interactions taking place in your application, but what about those times when you have something fairly repetitive?  The principles of DRY dictate you should move such repetitive code into a function.  That's easy to do if you want to keep everything within the scope of Polymer:

Polymer({
  is: 'my-element',
  properties: {},

  scopeFxn1: function() {
    console.log(this);  // just as you would expect in the Polymer scope
  },

  ready: function() {
    this.scopeFxn1();
  }
});

However, imagine if you have a bunch of different interactions you need to define as functions.  This could start to make your Polymer element and its JavaScript properties a bit long and messy-looking.  The first thought is to move these functions outside the scope of Polymer and possibly into a separate file containing your JavaScript logic.  However, note the caveat to that:


this doesn't propagate into your function.  Watch out!


If you define a function outside the scope of Polymer, and try to call it from within a function that is in the scope of Polymer, the browser will not know what this is anymore and that could prove very frustrating for you if you want to take advantage of any of the benefits this brings you, such as manipulating variables bound to DOM elements or updating properties of your data objects.

outsideFxn1: function() {
  console.log(this);  // nope, does not compute
},

Polymer({
  is: 'my-element',
  properties: {},

  ready: function() {
    outsideFxn1();
  }
});

One potential workaround: simply pass this as an argument from within the scope of Polymer to your external functions.

outsideFxn1: function(element) {
  console.log(element);  // same as this, but outside Polymer scope now!
},

Polymer({
  is: 'my-element',
  properties: {},

  ready: function() {
    outsideFxn1(this);
  }
});

This way, you can use element (or whatever you end up calling it) to update the data bindings on your UI and run the set() function on your objects in order to properly broadcast updates to their properties.

To be honest, I had this article waiting in the wings for a long time but posted it now simply because my upcoming features on ATmega fuses and retrocomputing reminiscence aren't quite ready yet, and I wanted to get something out for this week.  But since originally getting the idea for this article, I learned about Polymer behaviors.


Behave Yourself, You Big Group Of Functions!


Polymer behaviors are like mix-ins in other popular languages like Java where there is no concept of multiple inheritance but people still have the need to incorporate functions belonging to different classes of objects.  It's the best of both worlds; you can have all the functions you need in order to do all the operations you want to do on your DOM and your data, but without the weird side effects and useless baggage you would get if you had to do multiple inheritance as in C++.  And because Polymer behaviors are defined in the scope of Polymer in your application, the functions (whether you write them or not) in these behaviors understand the true meaning of this once they are initialized.  I'm not going to provide an example here because there are tonnes of other places where you could obtain them, including the link to the official Google doc above (as long as 1.0 is still relevant).

As such, you could probably pass this around between functions inside and outside the scope of Polymer, as described initially, but you know what they say about "when in Rome..."  I would argue it's wiser and more sustainable to stick to the conventions given to you by the framework.  If you're looking to attain DRY nirvana in your code and want to push functions out of the scope of any individual Polymer element you write, just implement the code in a Polymer behavior, save your this, and call it a day.

No comments:

Post a Comment