Showing posts with label Web. Show all posts
Showing posts with label Web. Show all posts

Wednesday, February 13, 2019

Polymer LitElement is Here!



I've been using Polymer to help me write web components for a couple months now. During that time I've been using Polymer's base class PolymerElement which among other things provides DOM templating and data binding.

Just the other day the Polymer team announced the official release of their new base class called LitElement. It is provides a more lightweight and simpler way to create web components as compared to the PolymerElement.

I decided to give it a try and this is what I learned.

First of all I really like the simplicity of the LitElement programming model. It's very lightweight and easier to understand than the old PolymerElement way of doing things. If you've ever used React you will find that using LitElement is almost exactly like creating components in React. In fact I think the Polymer team borrowed heavily from React (or maybe ripped them off depending on your point of view).

The two-way data binding of PolymerElement is gone and replaced by one-way data binding, a la React. The old two-way data binding felt more Angular, while the new on-way feels, as I said, more like React. You can't rely on changes to propagate up anymore. Now you need to use events to notify parent elements that something has changed. This is a little more work, but in the long term probably more simple and reliable.

PolymerElement's two-way data binding was sometimes complex to understand and required you to use all sorts of extra configuration to make it work. LitElement's one-way data binding is just simple and easy to understand. I'm not sure I would even call it data binding, really. Your element has properties and when they change your element is re-rendered.

There is the big difference, I think. In PolymerElement you had a static accessor where you defined your HTML template with data binding embedded into it. With LitElement you now have a render() method that gets called any time a property of your element changes. The render() method completely rebuilds the DOM structure of your element. Instead of embedded data binding you build a string that represents your DOM using template literals accessing your properties directly.

For example, here's what a data binding looks like in PolymerElement where "name" is a property of the element.

1
2
3
4
5
    static get template() {
        return html`
            <div>Hello, <span>[[name]]!</span></div>
        `;
    }

And here's what it looks like in LitElement.

1
2
3
4
5
    render() {
        return html`
            <div>Hello, <span>${this.name}!</span></div>
        `;
    }

Notice how we are building a string inserting "this.name" property as an embedded expression in the template literal rather than using a static binding "[[name]]".

This works because behind the scenes the LitElement creates get and set accessor methods for the properties of your element. Whenever a property changes it calls the render method so that the template string gets rebuilt. Then it figures out what changed and updates that portion of the light DOM on the page.

The great thing about using template literals is that you can now use any kind of JavaScript expression to build your DOM template, giving you a lot more power.

The consequence of this is that element properties must now be immutable. If you use React you are probably used to this. If one of your properties is an object or array you can't change an element in it and expect LitElement to notice that change and call render(). You will need to make a new copy of the object or array and change the value you want. If you want to know why this is better in the long run I suggest going through the React tutorial. They explain it in detail.

This also means that changes to properties in child elements will not automagically propagate up to the parent's properties. If you want to notify the parent of a change you will need to use custom events. Again, I suggest going through the React tutorial if you are not familiar with this way of doing things. (I told you they ripped off React :-p)

There is also a new way to bind properties to DOM element. For event handlers you use an @ before the event name and then an embedded expression using the name of the handler function. To define boolean attributes use a question mark. To bind to properties use a dot.


`<input type="text"
    @keyup="${this.keyPressed}"
    .value="${this.name}"
    ?hidden="${!this.isVisible}">`

In all cases you use embedded expressions for all of these bindings, even event handlers.

Using TypeScript

If you're like me and prefer using type annotations in your code, LitElement has support for TypeScript. It also allow you to use decorators to define your custom element and its properties. Use the customElement() decorator on your class and the property() decorator on properties. This will set up your element and properties for you making your code more terse.

1
2
3
@customElement('my-element')
export class MyElement extends LitElement {
  @property({type : String})  prop1 = 'Hello World';

I tried this but was having trouble getting it to work. In general I was having trouble getting the LitElement npm package to work nicely with TypeScript. The TS files in the npm package would not compile correctly because there was something missing that it relies on. This also affected the code completion in VS Code. [Edit] I did get type definitions and decorators to work correctly. Problem was that my compiler was not up to the version required for LitElement, which I think is 3.2. After installing latest everything worked fine. Also, you will need to set experimentalDecorators to true in your tsconfig file if you want to use decorators.

I'm really hoping TS support gets better over time. I didn't have these issues using PolymerElement.

Conclusion

In some ways LitElement feels like a step back because it's so much more primitive than PolymerElement was. But I feel like this is a good thing because it makes everything a whole lot simpler to understand and reduces complexity in your apps. I suggest you go ahead and start using LitElement now and hopefully TypeScript support will get better (or I will figure out what I'm doing wrong) in time.

Start learning LitElement here: https://lit-element.polymer-project.org/guide

Since LitElement is so much like React, and the documentation is limited for LitElement, I also suggest reading the React tutorial. https://reactjs.org/docs/hello-world.html

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!

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!