Thursday, November 15, 2018

Polymer Computed Properties

Polymer supports the concept of computed properties which allows you to create virtual properties whose values are calculated from other properties. If those properties that it depends on change the computed property will get recomputed.

However, there are a few gotchas when creating computed properties that you  need to know in order to successfully write your own. So let's do a simple example to explain.

Let's say we have a component with a name property and we want to change the layout depending on if the name is defined. So what we need is a computed property called hasName that returns true if the name is defined.

class MyElement extends PolymerElement {
  static get properties() {
    return {
      name: String,
      computedName: {
        type: String,
        computed: "_computeName(name)"
      }
    }
  }
  static get template() {
    return html`
      <span>[[computedName]]<span>`;
  }

To make a computed property you need to tell the property how to compute the value by specifyingit a function call, _computeName. Any parameters to the function will become dependencies of the computed property. In this case "name". So whenever name changes the _computeName() function will be called to recompute the value of computedName.

Here's the _computeName() function. It takes the name as a parameter and simply returns the name or "unknown" if it's not defined.

  _computeName(name) {
    return name || "Unknown";
  }

You might think we're done at this point, but if you were to run it now all you would ever get is "Unknown" for computedName, even if the name property changed. You would be pulling your hair out trying to determine why it doesn't work (not that I'm speaking from experience or anything ;-)).

The problem is that if the name property is undefined the first time the component gets created it will cause computedName to never get recomputed. Therefore we must give name a default value in the constructor. We'll just set it to empty string.


  constructor() {
    super();
    this.name = "";
  }

There! Now it should work properly.

Code hard!

Wednesday, November 14, 2018

Polymer Component Visibility Change Handling

I was working on a web app using Polymer's routing and I needed to know when the view changed to my component so that I could do certain things, like set the focus on a particular control.

There's a ready() method in the PolymerElement base class that you can override, but it only gets called the first time the component is shown. I needed it to get called every time the component is shown. So that doesn't work.

The key is to set selected-attribute on the iron-pages element. I set it to "visible". This will cause it to set a visible property on your view's component to true or false, depending on if the component is visible.


<iron-pages selected="[[page]]"
  attr-for-selected="name"
  selected-attribute="visible"
  role="main">
  <my-view name="my-view"></my-view>
</iron-pages>

Note that we don't need to set a visible attribute on the <my-view> component because it automatically gets passed into it.

Next thing to do is go into the view component and add a visible property definition whose type is Boolean. We also need to make it an observed property so we get notified when it changes.

class MyView extends PolymerElement {
  static get properties() {
    return {
      visible: {
        type: Boolean,
        observer: '_visibleChanged'
      }
    }
  }

Now whenever the visibility of the component changes it will call the _visibleChanged() method passing in true or false as the first parameter.

  _visibleChanged(visible) {
    if (visible) {
      // do something...
    }
  }

That's all there is to it. I figured it out from this article, which is (relatively) old and I think out of date for Polymer 3. It didn't quite work the way that article explained and this way seems a lot simpler.

Code hard!

Friday, November 9, 2018

A Simple TypeScript IOC Container

Motivation

When accessing external services from my web apps I like to use interfaces so I'm not tightly coupling a service implementation to where it's used. This has the benefit of making it easy to swap out the service implementation when writing unit tests. It's also very handy early on in the development process before you've implemented your service. You can create an implementation that just returns mock data and not get blocked waiting for the service.

To make this work you need a way to tell your application which implementation of the interface to use at run time. That's where an Inversion of Control (IOC) Container comes in. An IOC container allows you to use a technique known as dependency injection to get an instance of an interface without tightly coupling your code to a particular implementation.

If you've used Angular you know that it has dependency injection built in. But what if you're not using Angular? There are DI libraries out there for TypeScript, like InversifyJS. That's a pretty comprehensive library that even lets you use annotations to inject into constructors. This is great if you have deep objects with a lot of dependencies.

However, for most of my TypeScript apps all I need is a simple IOC container where I can ask it to give me an object for an interface. This is called a Dynamic Service Locator by Martin Fowler. So I created my own IOC container module for TypeScript based on this pattern.

Implementation

To start with we need a way to register interface implementations. For this we need a hash map to associate interface names to object constructors. Then we need a method to add them, which I will call bind(). Let's start by creating a module in its own file with a class called IocContainer.

class IocContainer
{
    private bindings = {};

    /** Binds an interface to an object
     * @param interfaceName Name of the interface
     * @param type Type to bind the interface to
     */
    public bind<T>(interfaceName: string, type: new () => T): IocContainer
    {
        this.bindings[interfaceName] = type;
        return this;
    }

The bind() method takes two parameters; the name of the interface and a constructor function of the class that implements that interface. Unfortunately there's no way to get the name of an interface from a generic definition in TypeScript so we must pass in the name of the interface as a string. All we do is add the mapping into the bindings object and we're done.

Next we need a way to get a concrete instance of an interface from the container. For this we need a get() method.

    /** Gets an instance for an interface as defined by bind() */
    public get<T>(interfaceName: string): T
    {
        let c = this.bindings[interfaceName];
        if (c)
        {
            return new c();
        }
        console.error(`There is no class bound to ${interfaceName}.`);
        return undefined;
    } 
}

The method takes one parameter; the name of the interface. We make it generic so it will automatically be cast to the correct type when we get it. All we do here is look up the constructor function for the interface name in the bindings object. Since it's a constructor function we can call "new" on it to create an instance of the object.

Note one limitation of this is that we can't pass parameters to the constructor. This is necessary because we don't know anything about the implementation! If the mapped object has its own dependencies on interfaces it too can use the container to get them.

The final thing we need to do is to export the container instance from our module. This makes the IOC container a singleton, which means there will be only one instance of it used everywhere.

var container = new IocContainer();
export default container;

Now for an example. Let's say we have an interface called ICustomerService and an implementation called MockCustomerService. Then we could register it like so.

import container from './services/IocContainer.js';
interface ICustomerService {
    getCustomer(id: number): Customer;
}
class MockCustomerService implements ICustomerService {
    getCustomer(id: number): Customer {
        return {...};
    }
}
container.bind("ICustomerService", MockCustomerService);

Finally, we get an instance of the interface from the container.

let service = container.get<ICustomerService>("ICustomerService");

Note that the get should be in a different file from where we did the binding. It would defeat the purpose to put the binding and get in the same file! Put the binding somewhere in your startup code. In this example, once we get our real customer service working all we need to do is change the binding and we're good to go.

Now let's say we wanted to write a unit test for the module that uses ICustomerService. All we would need to do is set up our binding differently in the unit test before testing the module.

class TestCustomerService implements ICustomerService {
    getCustomer(id: number): Customer {
        return {...};
    }
}
container.bind("ICustomerService", TestCustomerService);

Conclusion

Using an IOC container helps you avoid tight coupling to external services in your application by removing the construction of interface implementations from your code. This allows you to easily change the implementation later and provide different implementations in your unit tests.

Code hard!

Thursday, November 8, 2018

Web Components and Polymer

Web Components

I recently started working on a new web app and so started the process of trying to figure out what framework to use. Angular, Vue, React, something else? I remembered reading about web components so I thought I would take a look at that. I was pleasantly surprised by what I found.

I had thought that web components were another one of those things that was far off on the horizon, but it's actually implemented by most browsers at this point. And for the ones that haven't implemented it there are shims.

Web components are a set of web platform APIs that allow you to create new custom, reusable, encapsulated HTML tags to use in web pages and web apps.

  • Custom elements
  • Shadow DOM
  • ES Modules
  • HTML Template
Custom elements allow you to create new elements and add them to the browser.

Shadow DOM allows you to encapsulated style and markup in web components.

ES modules allow you to load and reuse JavaScript components.

HTML Template allows you to create unused HTML fragments that can be instantiated at runtime.

See this page for more details: https://www.webcomponents.org/introduction

Polymer

Web components sounded great to me. I love the idea of creating small reusable components that can all be linked together to create an application. As I started to do more research on how to get started I discovered Polymer. This was another thing I've heard of before but never paid much attention to.

Polymer is a set of tools created by Google that helps you build web components. It also has a library of web components you can use in your own apps. I don't want to call it a framework. It aims to be very lightweight and allow for progressive loading of apps so they start up quickly. This is in contrast to the big frameworks that are large and take a lot of resources to run. Polymer's philosophy is to let the browser do what it does best.

See the Polymer site for more information: https://www.polymer-project.org/

So I decided to give it a try and use it my new app. What I like about Polymer:

  • It gives you components to help you do routing and interacting with external services even when the user is offline
  • Allows you to define your component's style and HTML markup as a string in your component rather than having to build a shadow DOM by hand
  • Gives you two-way data binding to bind elements on the page to your data model
  • It's not that hard to learn
  • It's modular, use the pieces you want; it doesn't lock you into a opinionated framework
  • It's lightweight compared to some of those frameworks out there (yeah, I'm taking about you Angular)
  • Quick and easy to get up and running and create an app skeleton
Things I didn't like about it:
  • The documentation isn't that great; It's hard for me to find all of the properties and values for polymer components. I spend a lot of time trying to figure out how things work. There aren't that many examples out there either. Luckily it's not too complex and I can figure most things out.
  • I haven't found a way to extract the HTML markup and styles out of the JavaScript file. This makes it hard to get syntax highlighting and code completion. On the other hand I almost feel like this is also a good thing because it forces you to keep your components small.
  • Two-way data binding is not intuitive at all. There are all kinds of little gotchas that require "hacky" workarounds to get changes to propagate to the same object in other components.

Conclusion

So far I really like using web components. I'm getting the same feeling I had when I first heard about HTML 5, like this is the future of web development. Polymer has some things that I don't like but it really helps you build apps from web components quickly. But doesn't every web dev tool have things you don't like? The question is, are those things bad enough for you to move on to something else. We shall see.

I'm really excited about this new technology. I'll be using it as much as possible in the near future so I will keep you posted on my progress. Look for more posts as I learn more about web components and Polymer.

Code hard!