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!