The Lifecycle of an Aurelia Custom Element

I’ve been working with Aurelia pretty well recently, but I came across a problem early on. I had some code within my navigation bar (which is a custom element) and I wanted to manipulate the DOM on startup so that my initial state was correct. So, here was my code:

import {Authenticator} from "../lib/Authenticator";

export class NavBar {
    public constructor() {
        this.setIcon(Authenticator.isAuthenticated());
    }

    public setIcon(isAuthenticated: boolean): void {
        let appToolbar = document.getElementById("appToolbar");
        let elements = document.querySelectorAll(".sign-in,.sign-out");
        // Do something with the elements list
    }
}

This is pretty simple. My Authenticator object has a static method that returns true or false depending on if I am authenticated or not. It doesn’t work. I get an exception in the Javascript console at the highlighted line. The problem is that the DOM isn’t ready when the object is instantiated.

So, how do we fix this? Well, we need to know a little bit about the underlying code and the lifecycle functions that Aurelia uses on custom elements. Firstly, let’s do a little experiment. I replaced the code above with this:

import {Authenticator} from "../lib/Authenticator";

export class NavBar {
    public constructor() {
        //this.setIcon(Authenticator.isAuthenticated());
    }

    public attached() {
        this.checkit("attached");
    }

    public activated() {
        this.checkit("activated");
    }

    public created() {
        this.checkit("created");
    }

    public activate() {
        this.checkit("activate");
    }

    public canActivate() {
        this.checkit("canActivate");
    }

    public checkit(fn: string) {
        let appToolbar = document.getElementById("appToolbar");
        console.log("[NavBar] %s %o", fn, appToolbar);
    }

    public setIcon(isAuthenticated: boolean): void {
        let appToolbar = document.getElementById("appToolbar");
        let elements = document.querySelectorAll(".sign-in,.sign-out");
        // Do something with the elements list
    }
}

I basically guessed at the lifecycle functions on the initialization side of the fence. Each one calls checkit that then checks whether the DOM is ready (as referenced by looking for an element inside the DOM of the custom element) and then prints out the method that was called and the result of the check. Since I am printing the object, I can expand the object within the console and see what else is there. Here is what I got:

blog-0717-1

Note the highlighted lines. Aurelia calls created() first, but the DOM is not available at this point. It calls attached() next and then the DOM is ready – we have a DOM element as a result of our query.

It turns out I am not the only one here. There is a bug against the documentation asking that this be included in the documentation. But it begs the question – what other lifecycle methods do I have access to? The custom elements in Aurelia are based on Web Components (see an introduction here). the webcomponents.js polyfill has four lifecycle callbacks:

  1. createdCallback
  2. attachedCallback
  3. detachedCallback
  4. attributeChangedCallback

The createdCallback will call create(); the attachedCallback will call attached(). I’m guessing the detachedCallback calls detached() but I don’t have any documentation for that. The attributeChangedCallback is handled differently. When an attribute changes, a specific method for the attribute is called.

As a result of this investigation, I can now re-code my nav-bar.ts as follows:

import {Authenticator} from "../lib/Authenticator";

export class NavBar {
    public attached() {
        this.setIcon(Authenticator.isAuthenticated());
    }

    public setIcon(isAuthenticated: boolean): void {
        let appToolbar = document.getElementById("appToolbar");
        let elements = document.querySelectorAll(".sign-in,.sign-out");
        // Do something with the elements list
    }
}

Just remember – if you want to modify the DOM in your custom element, do it in the attached() method of your custom element class.

Integrating Font Awesome with Less in Web Components

I started to deal with Semantic Web for my header during the last article. Today is going to be all about the sign-in, sign-out and profile buttons. These are very simple buttons and their conversion should pose no challenge. However, there are some interesting things we can talk about with respect to Font Awesome – a large collection of icons that are generally used as a replacement set for the Glyphicons that come with Bootstrap.

I am going to create a web component called <s-signin> – it is normally constructed like this:

<span class="icon"><i class="fa fa-sign-in"></i> Sign In</span>

The class in the <i> brings in an icon from the Font Awesome collection. I need to use the LESS files that are supplied with Font Awesome to bring in the library mixins in much the same way that I did with Bootstrap. I also need to load the Font Awesome font – either directly using an @import in the CSS, or indirectly by linking the stylesheet. I’m going to use the latter since I expect multiple web components will utilize Font Awesome.

I need a HTML file for the s-signin.html web component:

<dom-module id="s-signin">
    <link href="../jspm_packages/npm/font-awesome@4.3.0/css/font-awesome.min.css" rel="stylesheet">
    <link rel="stylesheet" href="s-signin.css">
    <template>
        <span id="content">
            <span id="icon"></span>
            <span id="msg">Sign In</span>
        </span>
    </template>
</dom-module>
<script>
    Polymer({
        is: "s-signin"
    });
</script>

I’m not handling what happens when I click on this yet. That will come later. I also need a LESS file that will generate the s-signin.css file:

@import (reference) "../../style/site.less";
@import (reference) "../../../wwwroot/jspm_packages/npm/font-awesome@4.3.0/less/font-awesome.less";

#content {
    display: inline-block;

    #icon {
        .fa-icon();

        &:before {
            content: @fa-var-sign-in;
        }
    }

    #msg {
        color: red;
    }
}

This isn’t the final styling. However, it does show off how to import the Font Awesome library. There is one final thing I need to do. I need to exclude font-awesome from “inlining” in the vulcanization process. If I don’t do this, then font-awesome will be included three times – one for each component that uses it. I’ve altered the “build:components” task in Gulpfile.js like this:

gulp.task("build:components", [ "build:t_components" ], function() {
    // Vulcanize the element
    return gulp.src(path.join(paths.tmp, "**", "*.html"))
        .pipe(vulcanize({
            dest: paths.dest.elements,
            inline: true,
            excludes: {
                styles: [ "font-awesome" ]
            },
            strip: !DEV_MODE
        }))
        .pipe(gulp.dest(paths.dest.elements));
});

You will find the initial versions of the s-signin, s-signout and s-profile web components (styling only) at tag testweb-3. I just wanted enough in these to start working on the collapsible navigation menu web component. I’ll revisit these components again later on.

You can find the components that I’ve developed here at tag testweb-3.

A Polymer Image Carousel

I’m still working on my home page. What I want to do is create a Web Component (in Polymer – my preferred mechanism) to rotate images. I see this a lot on home pages and I think it would look cool. I’ve downloaded 4 fantasy wall papers in HD and resized them to be 1920×600 pixel resolution so that I have something to test this on. In an ideal world, these will change on a regular basis and have a nice transition between the different images.

On to the web component. I’ve adjusted my home page Areas/Main/Views/Home/Index.cshtml file to be the following:

@{ ViewBag.Title = "Home Page"; }

<div id="#Homepage">
    <div class="container-fluid">
        <div class="row">
            <dnd-carousel>
                <div class="s1"></div>
                <div class="s2"></div>
                <div class="s3"></div>
                <div class="s4"></div>
            </dnd-carousel>
        </div>
    </div>
</div>

@section htmlelements {
    <link rel="import" href="/jspm_packages/github/Polymer/polymer@0.8.0/dist/polymer.html">
    <link rel="import" href="~/elements/dnd-carousel.html">

    <style>
        html /deep/ .s1 {
            background-image: url("/images/backgrounds/homepage-s1.jpg");
        }
        html /deep/ .s2 {
            background-image: url("/images/backgrounds/homepage-s2.jpg");
        }
        html /deep/ .s3 {
            background-image: url("/images/backgrounds/homepage-s3.jpg");
        }
        html /deep/ .s4 {
            background-image: url("/images/backgrounds/homepage-s4.jpg");
        }
    </style>
}

The home page itself is relatively straight forward. The dnd-carousel will be my web component. Inside are four DIVs that I will rotate between.

Note that html /deep/ notation in the style sheet section. The child DIVs of the dnd-carousel element end up inside the shadow (or shady) DOM. This means that I have to cross that boundary between Shadow and Shady DOM to style those elements. I could have been explicit – adding a style property to the elements instead – especially considering that these are just images. The style sheet won’t be applied properly if I don’t include the html /deep/ syntax.

On to the web component. As normal, I’m putting my component under src/elements in a directory called dnd-carousel. There is a HTML file, a JS file and a LESS file. The basics are always the same. My HTML file is dnd-carousel.html:

<dom-module id="dnd-carousel">
    <link rel="stylesheet" href="dnd-carousel.css">
    <template>
        <div id="contentwrapper">
            <div id="carousel">
                <content></content>
            </div>
            <div id="carouselmarkers">
            </div>
        </div>
    </template>
</dom-module>

<script src="dnd-carousel.js"></script>

All I’ve changed here is the markup in the shadow DOM. I need a less file – dnd-carousel.less – to style this:

:host {
    display: block;
    width: 100%;
    height: 600px;
}

#contentwrapper {
    position: relative;
    width: 100%;
    height: 600px;
    left: 0;
    top: 0;

    #carousel {
        > ::content {
            display: block;
            width: 100%;
            height: 100%;

            div {
                display: none;
                width: 100%;
                height: 100%;
                background-repeat: no-repeat;
                background-size: cover;

                &.active {
                    display: block;
                    transition: all 1s ease-in-out;
            }
        }
    }
}

Note the active class. I’m going to add and remove this class from the elements as they come to the front and move to the back of my element. If the element is “active”, then it’s shown. If not, it isn’t.

Finally, I need a small Javascript file – dnd-carousel.js:

Polymer({
    is: "dnd-carousel",

    ready: function() {
        console.log("dnd-carousel: In ready()");
    }
});

With this markup, nothing is being shown. I can, however, check with the F12 Developer Tools that the right size and position is happening. Let’s start filling in the functionality:

Markers

In most of the carousels I see, there are numbers or blocks at the bottom – click on a number or block and you get taken to that image in the carousel. I’d like to emulate that. I added a carouselmarkers empty DIV into the HTML shadow DOM for this purpose. I now need to fill it in, which I do programatically:

    ready: function () {
        this.DIVSET = Polymer.dom(this).querySelectorAll("#carousel > div");
        if (this.DIVSET.length === 0) {
            console.log("No members - turning off");
            return;
        }

        for (var i = 1 ; i <= this.DIVSET.length ; i++) {
            var marker = document.createElement("span");
            marker.className = "marker";
            marker.innerHTML = i.toString();
            Polymer.dom(this.$.carouselmarkers).appendChild(marker);
        }
        Polymer.dom.flush();
    }

The first block gets all the DIVs that we passed in as children of the dnd-carousel and stored them for later. The next block constructs the elements I will need. Each digit will be added as a span with a class of marker.

Note the use of the Polymer.dom() API here. When you use this.$.id.appendChild(), it doesn’t add the style-scope necessary for using Shady DOM (which is the type of DOM used in webcomponents-lite.js that Polymer now uses). By utilizing the Polymer.dom() API for DOM interactions within the web component, that functionality gets implemented for us.

Of course, I’ll need some styling for this:

    #carouselmarkers {
        position: relative;
        top: -50px;
        width: 100%;
        text-align: center;
        z-index: 5;

        span.marker {
            border: 1px solid white;
            border-radius: 50%;
            color: white;
            padding: 0 4px;
            margin: 0 4px;
            cursor: pointer;

            &:hover {
                border: 1px solid #ff6a00;
                color: #ff6a00;
            }
        }
    }

This block is inside of the #contentwrapper at the same level as #carousel in the less file. I’ll also want to display the first carousel member to test this. Since I’ll be activating carousel elements, I may as well add a function to the Polymer object for that purpose:

Polymer({
    is: "dnd-carousel",

    ready: function () {
        this.DIVSET = Polymer.dom(this).querySelectorAll("#carousel > div");
        if (this.DIVSET.length === 0) {
            console.log("No members - turning off");
            return;
        }

        for (var i = 1 ; i <= this.DIVSET.length ; i++) {
            var marker = document.createElement("span");
            marker.className = "marker";
            marker.innerHTML = i.toString();
            Polymer.dom(this.$.carouselmarkers).appendChild(marker);
        }
        Polymer.dom.flush();

        // Reset the carousel active element
        this.activeElement = -1;

        // Display the first carousel
        this.displayCarousel(0);
    },

    // Private methods
    displayCarousel: function (idx) {
        if (idx === this.activeElement) {
            console.log("displayCarousel: Skipping as requested EL is the same as Active EL");
            return;
        }

        console.log("displayCarousel: displaying carousel element %d", idx);
        for (var i = 0 ; i < this.DIVSET.length ; i++) {
            var el = this.DIVSET[i];
            if (i === idx) {
                this.addClass(el, "active");
            } else {
                this.removeClass(el, "active");
            }
        }

        this.activeElement = idx;
    }
});

With this code and the styling applied, I can see the first element (the one labelled class="s1") and there will be the numbers 1 through 4 on top of the picture in white circles. If I roll over the numbers, they should turn orange. I’ve added some code from youmightnotneedjquery for addition and removal of classes as well:

    //#region CSS Class Handling Functions
    hasClass: function(el, className) {
        if (el.classList) {
            return el.classList.contains(className);
        } else {
            return new RegExp("(^| )" + className + "( |$)", "gi").test(el.className);
        }
    },

    addClass: function(el, className) {
        if (this.hasClass(el, className)) {
            return;
        }
        if (el.classList) {
            el.classList.add(className);
        } else {
            el.className += " " + className;
        }
    },

    removeClass: function(el, className) {
        if (!this.hasClass(el, className)) {
            return;
        }
        if (el.classList) {
            el.classList.remove(className);
        } else {
            el.className = el.className.replace(new RegExp("(^|\\b)" + className.split(" ").join("|") + "(\\b|$)", "gi"), " ");
        }
    },
    //#endregion

This is library code for me, but it’s so small that I include it where I need it.

Making the Markers Clickable

My next step is to make the markers clickable. The best way to do this is to add an event handler that calls my displayCarousel() method when the user clicks on the number elements. I can do this during the marker creation process in the ready() method. Here is the complete marker production loop:

        for (var that = this, i = 1 ; i <= this.DIVSET.length ; i++) {
            var marker = document.createElement("span");
            marker.className = "marker";
            marker.innerHTML = i.toString();
            Polymer.dom(this.$.carouselmarkers).appendChild(marker);

            marker.addEventListener("click", function (evt) {
                var newIndex = parseInt(evt.currentTarget.innerText);
                that.displayCarousel(newIndex - 1);
            });
        }
        Polymer.dom.flush();

The transition is a little harsh right now – I don’t think my transition CSS property is taking effect at the moment, but I can deal with that later.

Cycling the images

The other thing I want my carousel to do is to cycle through the images on a regular basis. For this, I want to add a property “interval” to my dnd-carousel, with a default timing of 30 seconds. Then I will use that in the ready() method to set up my event listener for transitioning the carousel. I’ll use the same displayCarousel() method to change the image. To handle the property, I have a new element within the Polymer object:

    properties: {
        interval: {
            type: Number,
            value: 30
        }
    },

You can check out the documentation on Polymer properties for more information. To set up the timer and call the displayCarousel() method, I added the following code to the ready() method:

        this.intervalTimer = setInterval(function () {
             var newIndex = (that.activeElement + 1) % that.DIVSET.length;
            that.displayCarousel(newIndex);
        }, this.interval * 1000);

I’ve already started tracking the active element (in this.activeElement), so all I need to do is increment the active element, adjust for the number of elements, then call displayCarousel().

Finishing off the Carousel

I did some more work before checking it in, but the basic functionality is there now. The things I did before checking in included:

  1. Highlighting the color marker when the image is shown
  2. Improving transitions between images by using opacity instead of display
  3. Some general code clean-up

You can get this code at tag cs-0.0.9. However, all the code is isolated to the src/elements/dnd-carousel directory, except for the view change on the home page.

Designing a Simple Polymer Web Component: Part 2 – Styling

In my previous post I created the foundation of my web component but it didn’t look how I wanted it to. Today I am going to finish off the web component visuals. Most of my work was with playing with the CSS. However, I bumped into some roadblocks that are worth highlighting.

Styling the content blocks

My initial CSS just had an embedded img and h1 selector. This is the wrong way to be selecting the stuff inside content and the Polymer documentation is quite explicit about it. Surroung the <content> block with a div that has a selector, then use a recipe. My characterName section became this:

.characterName {
    position: absolute;
    top: 70%;
    left: 0;
    width: 100%;
    height: 30%;
    text-align: center;
    vertical-align: middle;
    background-color: beige;

    &gt; ::content h1 {
        font-family: 'Segoe Script', cursive;
        margin: 2% 0 !important;
    }
}

Note the ::content – this is the selector nomenclature for the content block. I did something similar to the img tag as well so that it always fit within the characterImage content block.

Bootstrap, DOMReady and Scripts

One of the things I wanted to do was to make the element square. The work is a simple one-liner in Javascript:

elem.style.height = elem.offsetWidth + "px";

Placing it at the right point in the code is critical. My first attempt did it in the scripts section of Index.cshtml with jQuery, like this:

$(document).ready(function () {
    $("dnd-gridcharacter").each(function (idx, elem) {
        $(elem).css({ height: $(elem).outerWidth() + "px" });
    });
});

This does not hit the encapsulation requirement I had set for myself. My component should not have to rely on external scripts in order for it to work properly. I also had a problem with resizing. If the external app resized my element, then the element should resize accordingly to keep the square shape. Setting the height once was not enough.

After some trial and error, I ended up with the following code in my dnd-gridcharacter.js file:

    //#region Global Event Listeners
    domReady: function() {
        this.windowResize();
    },

    windowResize: function() {
        this.style.height = this.offsetWidth + "px";
    },
    //#endregion

    //#region Lifecycle event handlers
    ready: function() {
        console.log("dnd-gridcharacter::ready: %s %s", this.localName, this.id);

        var that = this;
        // Add event handler for DOMContentLoaded
        if (document.readyState != 'loading') {
            this.domReady();
        } else {
            window.addEventListener('DOMContentLoaded', function () { that.domReady(); });
        }

        // Add event handler for window resize
        window.addEventListener("resize", function() { that.windowResize(); });
    },
    //#endregion

This code does not work by itself. Let me go through it first. I have two methods in the object that will be called when the page DOM is ready and when the window gets resized. To implement those, I add an event listener on the DOMContentLoaded event in the ready() method. ready() is a Polymer event that fires once the element has been created and had the shadow DOM fully populated.

So why didn’t it work? When I ran my page the size of the element was not set. I added a console.debug to print the this.offsetWidth to verify this. It was as if styling had not been rendered yet. Was I capturing the right event?

I tend to wander the Internet when this happens. In this case I went to the Mozilla Developer Network and started researching events. I started reading the description for the DOMContentLoaded event to ensure that I was indeed using the right event. At the top of the page is this warning:

blog-code-0417-2

The implication is less than clear here. If you don’t have a script at the bottom of your page, then the DOMContentLoaded event is fired early – before the page is fully rendered out. The DOM is there but not the styling. I wanted details of the styling.

The way to fix this is to include a script at the bottom of the main page. I wanted to bring in Bootstrap and their grid component anyway. Bootstrap has a dependency on jQuery. Use jspm install bootstrap to bring in the library and include it in the Layout.cshtml file:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link href="~/jspm_packages/npm/font-awesome@4.3.0/css/font-awesome.min.css" rel="stylesheet">
    <link href="~/jspm_packages/github/twbs/bootstrap@3.3.4/css/bootstrap.min.css" rel="stylesheet">

    <!-- Required Polyfills -->
    <script src="~/jspm_packages/npm/webcomponents.js@0.6.0/webcomponents-lite.min.js"></script>

    <!-- Components used -->
    @RenderSection("htmlelements", required: false)

    <title>@ViewBag.Title</title>
</head>
<body>
    <div class="flex-container">
        @RenderBody()
    </div>

    <!-- Requirement for BootStrap -->
    <script src="~/jspm_packages/github/components/jquery@2.1.3/jquery.min.js"></script>
    <script src="~/jspm_packages/github/twbs/bootstrap@3.3.4/js/bootstrap.min.js"></script>

    <!-- Page Scripts -->
    @RenderSection("scripts", required: false)
</body>
</html>

Relative Font Size

Another task that took me a while to figure out was the sizing of the character name. The dnd-gridcharacter element can be any height, so I had to account for the size differences. A quick search on Stack Overflow brought up eight questions that fit my criteria, each – of course – with a slightly different variation. I went for the simplest, which is to calculate the font size. To do this, I used the following code in the resizeWindow() method of my dnd-gridcharacter.js file:

    windowResize: function() {
        // Make the object square by setting height == width
        this.style.height = this.offsetWidth + "px";

        // Make the character name font fit inside the containing DIV
        var textHeight = Math.floor(this.offsetHeight * 0.12);
        Polymer.dom(this).querySelector("h1").style.fontSize = textHeight + "px";
    },

You may be wondering why 0.12 as the multiplier? The character name section is 30% of the overall component height. I want to allow for some padding around the text (around 2% of the room), which brought it to 26% of the overall component height. I wanted to allow for two lines, like in my example, which brough it to 13%. When I looked at the result, I decided that the line height may be a little different than the font size, so I reduced it just a little more. It’s not very scientific but the result looks good.

A quick note about the Polymer.dom call. Polymer needs to deal with the Shadow DOM and, for those browsers without Shadow DOM, a fake version that they call “Shady DOM”. The Polymer.dom() abstracts the difference away from you, so it works in whatever browser you need. It has a restricted subset of functions for finding and manipulating the DOM – I’m sure that will be expanded over time so that you can do anything in the Polymer.dom() API that you can in the regular DOM API. There are some caveats with using Polymer.dom() to manipulate the DOM, so make sure you read the documentation first.

Icons and Observers

The last thing I needed to do was to implement the icons. I’ve got my icons from the Windows 8 Metro Invert Iconset. This set is large and has all sorts of things. I’m not sure I’ll stick with it. I’ve downloaded two icons – one for sharing and one for groups. Both icons are PNG format and 128x128px. I added them into my dnd-gridcharacter.html file like this:

<dom-module id="dnd-gridcharacter">
    <link rel="stylesheet" href="dnd-gridcharacter.css">
    <template>
        <div class="content-wrapper">
            <div id="partyIcon"><img src="/style/icons/group.png"></div>
            <div id="shareIcon"><img src="/style/icons/share.png"></div>
            <div class="characterPicture"><content select="img"></content></div>
            <div class="characterName"><content select="h1"></content></div>
        </div>
    </template>
</dom-module>

<script src="dnd-gridcharacter.js"></script>

Note that I have changed the class to an ID for the partyIcon and shareIcon DIVs. This is because I want to reference them in code explicitly. I’ve also changed the CSS to match this change. I have not wired the logic up at this point, and I had to add a little bit of CSS to get the icons to be resized and the right size.

I want to ensure that the party and share icons only appear by default when those attributes are set. To do this, I’ve added a couple of famous characters to my grid in Index.cshtml:

@{ ViewBag.Title = "Home Page"; }

<h1>Home Controller</h1>

<div class="container-fluid">
    <div class="row">
        <div class="col-lg-2 col-md-2 col-sm-4 col-sx-12">
            <dnd-gridcharacter party shared>
                <img src="/style/avatars/gnome/wizard.jpg">
                <h1>Finckeewack<br>Clusterbomb</h1>
            </dnd-gridcharacter>
        </div>
        <div class="col-lg-2 col-md-2 col-sm-4 col-sx-12">
            <dnd-gridcharacter party>
                <img src="/style/avatars/personal/elminster.png">
                <h1>Elminster<br>Aumar</h1>
            </dnd-gridcharacter>
        </div>
        <div class="col-lg-2 col-md-2 col-sm-4 col-sx-12">
            <dnd-gridcharacter shared>
                <img src="/style/avatars/personal/drizzt.jpg">
                <h1>Drizzt<br>Do'urden</h1>
            </dnd-gridcharacter>
        </div>
    </div>
</div>

@section htmlelements {
    <link rel="import" href="/jspm_packages/github/Polymer/polymer@0.8.0/dist/polymer.html">
    <link rel="import" href="~/elements/dnd-gridcharacter.html">
}

I’ve downloaded some different images to represent them. Elminster should have the party icon but not the shared icon and Drizzt should have the share icon but not the group icon.

Right now, I don’t know when, or indeed IF, the observers are called. To determine this, I put a console.log statement to find out:

blog-code-0417-1

Based on the console, the observers are called before the element is “ready” even on element creation, so I don’t have to do any work in the ready() method. The observer just needs to change the display property based on whether the newValue is true or not, like this:

    //#region Property Observers
    partyObserver: function (newValue, oldValue) {
        this.$.partyIcon.style.display = newValue ? "block" : "none";
    },

    sharedObserver: function (newValue, oldValue) {
        this.$.shareIcon.style.display = newValue ? "block" : "none";
    }
    //#endregion

Note the use of this.$ – this is a collection of the elements with IDs in my component and I can use this to easily identity the various blocks in my shadow DOM.

The Final Result

The final result looks like this on screen:

blog-code-0417-1

This is pretty much what I envisioned when I first created the powerpoint version (for reference – I’ve shown it again below):

blog-code-0416-2

The difference is that I’ve done all the code and the component is fully responsive to changes in sizes and can be instantiated as many times as I want. I may need to do more to implement other items in my application, but this is looking good now.

This is probably a baseline web component for me. It isn’t the very basic version that I implemented in the polymer-helloworld component, but it doesn’t have a lot of bells and whistles either. As a result, I’ll be able to use this as a model going forward.

Got an idea on how to improve my web component? Let me know in the comments.

I’ve checked in my work on this web component using tag cs-0.0.4.

Designing a simple Polymer Web Component: Part 1 – Setup

I thought I’d go through my design process for a web component. There are three pieces to a web component. The HTML markup, the styling and the Javascript to implement it. My side project needs several widgets that I will implement in web components. Getting a good handle on the design process will assist when it comes to that.

Step 1: Have a good idea on what you want to do

It seems silly, but do you know what you want the end product to look like? My top level page is going to be the cast of characters that I have access to. Each one can be a member of a party and/or shared with other people. I would like it to have a picture, the characters name and indicators for whether it is a member of a party or shared. My first attempt is to borrow images from the Internet to design where things are going:

blog-code-0416-2

This was done with PowerPoint – I had it handy. However, you can use any tool you care to. Got gimp? An excellent choice. How about paint.net? Another excellent choice. Just use what you know and have access to.

The idea is that the left hand top icon indicates that the character is a member of a party. The right hand top icon indicates that it is shared. The picture in the middle will either be a default one based on the characters race and class or I will allow the user to upload one. One of the friends I game with draws characters for each of us. Finally, the name of the character is at the bottom. I should be able to click on the whole thing and get the same action – the character sheet is opened. If I roll over the two icons at the top I should be able to see that it is shared (and with whom) and who else is in the party.

Decide on a rough layout

Now that I have the vision in my head, it is time to lay it out. Well, almost. The next step is to take all the imagery out of the equation and decide what blocks go where. I do this in PowerPoint as well, just to get it fixed in my mind. It helps when I am doing the style sheet. Here is what I drew up for my character grid component:

blog-code-0416-1

I’ve got a square block which will be dependent on the size of the screen – I’m a fan of responsive design. The icons will be 20% of the size of the block, the character will be 60% of the size. At the bottom is a section for the name of the character.

Decide how to lay out the HTML

When I include this character on the grid I want to specify whether it has the icons, the location of the picture and the name of the character. Something like this:

<dnd-gridcharacter party shared>
    <img src="/avatars/standard/dwarf/cleric.png">
    <h1>Finckeewack<br>Clusterbomb</h1>
</dnd-gridcharacter>

This layout provides everything I could potentially want from the component.

Produce the Template

The next step is to start lay down the template. I’m not going to worry about styling or interactivity at this stage – I just want to start producing the HTML. First of all, I create a directory under my src/elements directory for the element. Then I produce the dnd-gridcharacter.html file:

<dom-module id="dnd-gridcharacter">
    <link rel="stylesheet" href="dnd-gridcharacter.css">
    <template>
        <div class="content-wrapper">
            <div class="party-icon"></div>
            <div class="share-icon"></div>
            <div class="character-picture"><content select="img"></content></div>
            <div class="character-name"><content select="h1"></content></div>
        </div>
    </template>
</dom-module>

<script src="dnd-gridcharacter.js"></script>

Here I just used the outline I prepared for the polymer-helloworld.html file and inserted what I think will be my content. Everything is wrapped into a content-wrapper and then I have my four DIV areas to hold the four things I am interested in positioning.

I also need to add in a dnd-gridcharacter.js file that instantiates the web component:

Polymer({
    is: "dnd-gridcharacter",

    properties: {
        party: {
            type: Boolean,
            value: false,
            observer: "party-observer"
        },
        shared: {
            type: Boolean,
            value: false,
            observer: "shared-observer"
        }
    }
});

You saw the is: property in the Polymer initialization object when I was showing off the polymer-helloworld web component. However you may not have seen the properties. This has changed since the Polymer 0.5 days. In the prior version you could declare properties in the polymer-element element in the HTML file. Properties are now explicitly defined in the Javascript, along with their type and default value.

I also get to decide what type of data-binding I want. Inbound data-binding is implemented by using an observer. The string is the method to be called when a change is noticed. Outbound data-binding is implemented by using notify. In this case, an event is fired when the value of the property changes. You can also do two-way data-binding. Just implement both the observer and notify.

For this application I’m only interested in the inbound data-binding. I won’t be changing the state of party or shared myself. In order to implement the inbound binding, I’ll need to define two functions:

    partyObserver: function (newValue, oldValue) {
        // ... change the party icon according to newValue
    },

    sharedObserver: function (newValue, oldValue) {
        // ... change the shared icon according to newValue
    }

These are defined inside the Polymer object just like is and properties.

Initial Styling

I also want to implement some initial styling based on my PowerPoint. It won’t be right the first go around but it will be something I can play with. I’ll put the stylesheet in dnd-gridcharacter.less:

.content-wrapper {
    display: block;
    position: relative;
    width: 100%;
    height: 100%;
    border: 1px dotted goldenrod;
    padding: 20px;
    background-color: teal;

    .party-icon, .shared-icon {
        position: absolute;
        top: 0;
        width: 20%;
        height: 20%;
        border: 1px dotted red;
        z-index: 5;
        background-color: white;
    }

    .party-icon {
        left: 10%;
    }

    .shared-icon {
        right: 10%;
    }

    .character-picture {
        position: absolute;
        top: 10%;
        left: 20%;
        width: 60%;
        height: 60%;
        z-index: 3;
        border: 1px dotted green;
        background-color:cadetblue;

        img {
            width: 100%;
            height: 100%;
        }
    }

    .character-name {
        position: absolute;
        top: 70%;
        left: 0;
        width: 100%;
        height: 30%;
        text-align: center;
        vertical-align: middle;
        background-color: beige;

        h1 {
            font-size: 36px;
            font-family: 'Segoe Script', cursive;
            color: #33;
        }
    }
}

This is most definitely the wrong CSS to be using. I’ve given each element a border color and a background. None of the icons or pictures are “there”. I’m hoping the positioning is somewhat good to go. What this does is give me a mechanism by which I can begin to alter the CSS within the browser so I can get it right.

A better designer than I would probably do this process differently. I’ve found I am terrible at understanding what CSS properties affect positioning and how they work when in a responsive display.

Checking it out

I’ve changed my Areas/Main/Views/Index.cshtml file to bring in an example of the work:

@{ ViewBag.Title = "Home Page"; }

<h1>Home Controller</h1>

<div style="width: 500px; height: 500px; position: relative">
    <dnd-gridcharacter party shared>
        <img src="https://placeimg.com/300/300/people/grayscale">
        <h1>Finckeewack<br>Clusterbomb</h1>
    </dnd-gridcharacter>
</div>
@section htmlelements {
    <link rel="import" href="/jspm_packages/github/Polymer/polymer@0.8.0/dist/polymer.html">
    <link rel="import" href="~/elements/dnd-gridcharacter.html">
}

Note that I’m using a placeholder image site to load an example image. I’ll replace that at a later data – it just isn’t important for me right now. Here is what the initial version looks like:

blog-code-0416-3

The good news is that it isn’t terrible. There are some things that are certainly wrong with it. Aside from the borders and colors (that I knew were going to be wrong) the image is the wrong size and in the wrong place, the text is the wrong size and the shared icon area is just not there – probably overlapping the party icon area.

Some things live to see another day. Tomorrow I’ll take a look at the debugging tools for CSS available in Chrome, add in some code to make multiple versions of the grid character so I can see how it scales and generally finish off the component.

Comparing Web Components: Part 3 – Bosonic and X-Tags

Aside from Polymer (which was relatively easy and definitely going in the right direction) there are two other libraries listed on the webcomponents.org website – Bosonic and X-Tags. In this article I will review each one and what it took to get them working.

TL;DR – I was disappointed in all the libraries. This may come off as my not understanding the libraries or just loving Polymer too much. I can live with myself for that.

X-Tags

I was actually disappointed in this library. I’m sure some people will get offended when I say this, but the X-Tag library is just a crude wrapper around the web components platform library (webcomponents.js) and does nothing more for me than the original vanilla javascript version. At least with the vanilla javascript version I could see what was happening. Not so fast on this one.

Firstly, there are things I could not get around. If I included webcomponents-lite.js, then I got an error in the x-tag-core library. No way around it – the current version of X-Tag doesn’t work with the current version of webcomponents.js. This made it unlikely that I will use X-Tag to start with.

Then there is the code. I set up a simple one page web component – it had a template, stylesheet and some Javascript. Here is the Javascript code:

        (function () {
            xtag.register('xtag-helloworld', {
                prototype: Object.create(HTMLElement.prototype),

                lifecycle: {
                    created: function () {
                        console.log("xtag-helloworld: lifecycle.created");
                        var tpl = document.getElementById("XTagHelloWorld").content;
                        this.appendChild(tpl.cloneNode(true));
                    }
                }
            });
        })();

Is this really any better than the vanilla JS version?:

(function () {
    // Find the template for this HTML element
    var template = document.currentScript.ownerDocument.querySelector("#noLibHelloWorld");

    // Create the prototype for the new HTML element
    var elementPrototype = Object.create(HTMLElement.prototype);

    // Set up lifecycle callbacks for the new HTML element
    elementPrototype.createdCallback = function () {
        // Create the shadow DOM and clone the template into the shadow DOM
        var shadow = this.createShadowRoot();
        shadow.appendChild(template.content.cloneNode(true));
    }
    document.registerElement("nolib-helloworld", { prototype: elementPrototype });
})();

In my eyes, this is practically identical. So what is the library buying me?

Finally, documentation – there is a single page of documentation. Not once does the page provide a complete end-to-end example. I did check out the examples in the x-tag github repo. Not one of the examples showed usage in a HTML Import – it was always a JS and CSS file together. Interesting, not one showed templates either – another core concept of web components.

Bosonic

The Bosonic library was doomed from the start. Rather than embrace web components (and utilize the polyfill), the Bosonic library decided to transpile the web components into a CSS and JS file that you could then include in your project. This is reasonable enough had they supported gulp – they don’t. This would actually add another step to my process:

  1. Turn the HTML/LESS/JS files into files appropriate for a HTMLImport
  2. Vulcanize the files so I can import them with one file
  3. Bosonize them so I can split them apart again

That extra step isn’t what I envisioned. The code is reasonable. For instance, in the case of the work I have been doing, here is the code:

        ({
            createdCallback: function () {
                var root = this.createShadowRoot();
                root.appendChild(this.template.content.cloneNode(true));
            }
        });

As with X-Tags, I can tie this back directly to the Vanilla JS version. Why do I need to do this scaffolding? Can’t the library detect I have a template and just do it? At least this library had support for templates and you could write the code in a single HTML file (although it was broken up for production usage).

In terms of documentation, I’d rate Bosonic as better than X-Tags but nowhere near as good as Polymer. The tutorial is a nice walk-through and introduces the major things you would need to do in digestible chunks, making it easy to get into Bosonic.

On the downside, I just don’t like their approach.

SkateJS

Given my disappointment with the former libraries, I decided to take a look elsewhere on the Internet to see if I could come up with anything comparable. SkateJS is a replacement for webcomponents-lite.js. Rather than use the W3C Web Components spec, it’s decided to use its own API.

The Verdict

There are really two answers here:

  1. If you don’t mind taking a dependency on a library including an API that isn’t stable yet and could change (in fact, it just did), then Polymer is probably your best bet. It does the scaffolding for you and has good documentation.
  2. If you care about the Polymer library dependency and the moving API target, take a dependency on the webcomponents-lite library and use the Web Components spec and Vanilla Javascript.

This is just my personal opinion. I highly recommend you do your own research. Take a simple component you are writing – maybe as a jQuery plugin or just a module of another app, and write it in each of the frameworks. You will figure out for yourself pretty quickly which one you prefer.

I’m going to take a bet that Polymer has gone through the major upheaval with the 0.8 release. Certainly, the information coming out of the project indicates this is so. There may be some refactoring to do when Polymer reaches 1.0, but it’s likely to be minimal. As a result, I’m taking that dependency on Polymer for my web components.

One thing to note about my project is that it is an ASP.NET MVC project; not a SPA project. As a result, the web components that I use are likely to be visual and relatively simple. I am not going to be using a multi-page router in a web component, for example. You should think about your own project and its needs when doing this research to ensure you get the best possible solution for your project.

Comparing Web Components: Part 2 – Polymer

Since I’ve had a little bit of time with Polymer, I decided to do that library next. However it has had a significant upgrade since my time with it. Version 0.8 has broken just about everything so don’t expect it to just “work”. There is a migration guide, however, and the API does a lot more for you, so it’s an improvement all round.

The Polymer folks like Bower for their distribution platform. This is a shame as jspm doesn’t support bower just now. However it does support GitHub just fine, so I can use that to install Polymer:

jspm install github:Polymer/polymer

Make sure it brings down Polymer v0.8.0

blog-code-0414-1

There are a couple of other changes that are necessary too. Firstly, Polymer uses the webcomponents-lite library. This is included in the webcomponents.js download from yesterday but it’s a different file. My Areas/Main/Views/Layout.cshtml file now looks like this:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link href="~/jspm_packages/npm/font-awesome@4.3.0/css/font-awesome.min.css" rel="stylesheet">

    <!-- Required Polyfills -->
    <script src="~/jspm_packages/npm/webcomponents.js@0.6.0/webcomponents-lite.min.js"></script>

    <!-- Components used -->
    @RenderSection("htmlelements", required: false)

    <title>@ViewBag.Title</title>
</head>
<body>
    <div class="flex-container">
        @RenderBody()
    </div>
    @RenderSection("scripts", required: false)
</body>
</html>

Additionally I’ve altered my Areas/Main/Views/Home/Index.cshtml as follows:

@{ ViewBag.Title = "Home Page"; }

<h1>Home Controller</h1>

<nolib-helloworld>No Library</nolib-helloworld>
<polymer-helloworld>Polymer</polymer-helloworld>

@section htmlelements {
    <link rel="import" href="~/elements/nolib-helloworld.html">

    <link rel="import" href="~/jspm_packages/github/Polymer/polymer@0.8.0/polymer.html">
    <link rel="import" href="~/elements/polymer-helloworld.html">
}

As with the no library version, I’ve created a directory src/elements/polymer-helloworld to store my custom element. Note that I am bringing in the Polymer library before the polymer-helloworld.html component, as a dependency. Within that directory, I’ve created a polymer-helloworld.less file with the following contents:

.outer {
    border: 2px solid #777;
    border-radius: 1em;
    background: blue;
    font-size: 20pt;
    width: 12em;
    height: 7em;
    text-align: center;
}

.boilerplate {
    color: white;
    font-family: sans-serif;
    padding: 0.5em;
}

.name {
    color: black;
    background: white;
    font-family: "Marker Felt", cursive;
    font-size: 45pt;
    padding-top: 0.2em;
}

Note that I’ve just changed the color of the “name badge” – this is so that I can distinguish between the CSS elements. If the CSS for each custom element is distinct (as it should be with Shadow DOM) then one name badge will be blue and one will be red. My next file is the polymer-helloworld.html file:

<dom-module id="polymer-helloworld">
    <link rel="stylesheet" href="polymer-helloworld.css">
    <template>
        <div class="outer">
            <div class="boilerplate">
                Hi! My name is
            </div>
            <div class="name">
                <content></content>
            </div>
        </div>
    </template>
</dom-module>

<script src="polymer-helloworld.js"></script>

Finally, the polymer-helloworld.js file is almost non-existent as the library takes care of most of the scaffolding for me:

Polymer({
    is: "polymer-helloworld"
});

Once this is done, a build of the components is possible. Check out the wwwroot/elements/polymer-helloworld.html file.

In development, this comes at a cost. Webcomponents-lite.js is 77K (although the full library is 258K – ouch!). In addition, polymer brings in 51 files. This is bound to be slow when loading over the Internet. We can speed it up by using vulcanize on Polymer itself. This is part of the production build of polymer and is distributed by bower in this fashion. Completing the build yourself is not a hardship however. Just bring up a PowerShell prompt and run the following:

blog-code-0414-2

I also need to alter the Index.cshtml file to bring in the vulcanized version instead of the source version:

@{ ViewBag.Title = "Home Page"; }

<h1>Home Controller</h1>

<nolib-helloworld>No Library</nolib-helloworld>
<polymer-helloworld>Polymer</polymer-helloworld>

@section htmlelements {
    <link rel="import" href="~/elements/nolib-helloworld.html">

    <link rel="import" href="/jspm_packages/github/Polymer/polymer@0.8.0/dist/polymer.html">
    <link rel="import" href="~/elements/polymer-helloworld.html">
}

This knocks down the imports to one 87K import instead of 51 files covering about the same space. More importantly, the load time of the page was down about 70% on my system.

Comparing to the Vanilla Javascript version

When it comes to code, there really is no comparison. The amount of code I had to write was practically nothing when I used the Polymer library. That means that all the scaffolding was done for me and I could concentrate on the code necessary to implement the component.

The CSS/LESS code is just the same and there is no practical difference between the templates. The code reduction comes at a cost but I expect this is a cost I can live with.

The Polymer version of my application is on GitHub and tagged as cs-0.0.3.