Showing posts with label Modules. Show all posts
Showing posts with label Modules. Show all posts

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!

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!


Saturday, May 7, 2016

Testing TypeScript Modules with Jasmine

In my previous post I talked about using TypeScript/ES6 modules. I have been thinking about making the transition from namespaces to modules, but there were a few things I needed to work out first. First of all how to load them (native ES6 support isn't widely available at the time of this writing). In the previous post I talked about using a library called Require1k (http://requirejs.org/) as the module loader. As the name suggests it is only 1kb in size yet provides the basics you need for CommonJS module loading. So far it does everything I need.

The next thing I needed to work out was how to package up my application when it was ready for release. In that post I also talked about using WebPack in Node.js to combine and minify the application modules.

The final thing I needed to work out was how to get my Jasmine (http://jasmine.github.io/) unit tests to run against modules. This was giving me a lot of trouble; it's not as easy as you might think. Jasmine was built in such a way that it expects all of your files to be loaded using script tags, then it runs when the page onload event fires. Unfortunately when using a module loader things don't happen that way. The module loader doesn't load your modules until after the page is loaded.

So for the remainder of this post I'll show how I got my Jasmine tests to run using the Require1k module loader. Although I'm using a specific module loader there shouldn't be any problem using the same principles for any other CommonJS module loader library as they most all work the same way.

Creating the Unit Tests


The first thing we need to do is create unit tests. I won't go into how to write Jasmine tests. There are plenty of good examples on their web site. The only difference is that you will need to use import statements to reference the modules you want to test.

For example, say you want to test module1.ts. Then your unit tests might look something like this.

/// <reference path="../scripts/typings/jasmine/jasmine.d.ts" />
import * as Module1 from "./module1";
describe("When testing module1", () =>
{
    it("should be true when something happens", () =>
        expect(Module1.something()).toBeTruthy());
});

First we have a reference to the Jasmine type definition file (because as TS programmers we like to have better tooling and autocompletion). Then we import everything from module1. Then we define the Jasmine specs for the unit tests. It's that simple ;-).

One important thing to point out here: your unit tests are modules too. Once you decide to use modules everything needs to be a module. Our unit tests need to load the modules they test, therefore they have to be modules for the module loader to pick up the imports and load them.

The Bootstrap File


Now we need a bootstrap file. This is the main file that gets things going. We will call this file unit-tests.ts.

declare function require(s: string): void;
 
require("./module1.spec");
require("./module2.spec");
 
jasmine.getEnv().execute();

The first thing we did was to declare the require function. This is the function defined by the Require1k library (or any other library for loading CommonJS modules) to load a module file. Without this we would get errors from the compiler because it doesn't know what "require" is.

Next we use require to load all of the unit test modules. In this case we have unit tests for two different modules.

I like to name my unit test files with the name of the module it's testing and a ".spec.ts" extension. This way you know exactly which module it's for and you know it's a Jasmine test file (Note: unit tests are known as specs in Jasmine).

The last line is necessary to tell Jasmine to start running the tests. As I stated above, Jasmine will look for any unit tests and run them on page load. But since this is all happening after page load we have to explicitly tell it to run again.

You might be asking yourself: why not use import statements to load all of the unit test files? That's a valid question. The unit tests are modules, I said so previously. The answer is: because nothing is being exported from the unit test modules. All they do is define Jasmine specs. Therefore if you tried to import them the TypeScript compiler would see there's nothing being exported and just skip it. Then your unit tests wouldn't load and you would be wondering what's going on (not that I'm speaking from experience or anything like that).

The Host File


The final thing we need to do is set up the host HTML file to run the unit tests. It will have all of the files for jasmine, of course. Then it will need the script tag for Require1k.

<link href="../Content/jasmine/jasmine.css" rel="stylesheet" />
<script src="../Scripts/jasmine/jasmine.js"></script>
<script src="../Scripts/jasmine/jasmine-html.js"></script>
<script src="../Scripts/jasmine/boot.js"></script>
 
<script src="require1k.js" data-main="./unit-tests"></script>

The "data-main" attribute tells Require1k what the main or start file is. In this case unit-tests.js which we created above. This will define the starting point of the module loader. From there it will load the unit test modules which will in turn load the modules they are testing.

Conclusion


The trick to testing external modules with Jasmine is to have a single bootstrap file that loads all of the unit test files and to tell the module loader where to find it. Then the key to making it work is to tell Jasmine to run again after all of the tests and modules have been loaded.

Switching from internal namespaces to external modules in TypeScript requires a whole new way of looking at things and some new ways of doing things that you're not used to. You just have to learn a few new tricks to get it to work across the board from your source code to your unit tests.

I think in the end using modules is the way of the future for TypeScript, and JavaScript. Once ES6 is fully supported by all browsers and able to provide native module loading things will be a lot easier and modular JS will become more popular.

<jmg/>

Wednesday, April 6, 2016

Packaging Web Apps with Node.js

There comes a time when you're at the end of developing your awesome TypeScript/JavaScript web app that you need to think about releasing it into the wild. If you've been using good programming practices you should have split your app into multiple files to make it easier to manage and maintain. Only problem is, it's not a good practice to serve up a ton of files when the user runs your app. The best practice in this scenario is to package up your JavaScript files into one file and only serve that one file.

In this post I will show you how to use TypeScript/ES6's module loading system and Node.js to make it easy to package your web app's files into one file for release while allowing you to develop without packaging. We will use TypeScript's module system to set up file dependencies. Then we will use Require.js to load those modules in the browser. Finally we will use Webpack to package up all of the files into a single JS file and compress it for release.

Prerequisites: I'm assuming you know TypeScript and Node.js basics, but even if you don't you should be able to understand the concepts and use them as a jumping off point to learn more.

The Modules


First of all lets look at how external modules work in TypeScript/ES6. For this example we are going to create three separate TS files, each of which depends on the next one. Two of the files will be modules and the other will be the main application file.

The first file module2.ts exports a class named MyClass2 with a public method getText().

export class MyClass2
{
    public getText(): string
    {
        return "Hello from module 2";
    }
}

Nothing special yet. We just have a class with a method.

The next file is module1.ts. It is a bit more interesting.

import * as module2 from "./module2";
 
export class MyClass1
{
    public getText(): string
    {
        let c = new module2.MyClass2();
        return "Hello from module 1 " + c.getText();
    }
}

At the top of the file we import our module2 module that was defined in module2.ts. Now this file depends on module2.ts. This allows us to use the exported members of module2.ts in module1.ts. This syntax follows the ES6 standard for importing modules.

Next we define the contents of module1. We export a class called MyClass1. In the getText() of this class it creates and instance of MyClass2 from module2 then calls its getText() method and concatenates the messages together.

That's how we do external modules in TypeScript and ES6. Pretty simple, right?

Now we need a main app file for our starting point, which in this case is app.ts.

import {MyClass1} from "./module1";
 
namespace WebpackModules
{
    export class MyApp
    {
        constructor()
        {
            let c1 = new MyClass1();
            var el = document.getElementById('content');
            el.innerText = "MyApp started " + c1.getText();
 
        }
    }
}
 
var app = new WebpackModules.MyApp();

We begin with an import of module1.ts. Now this file depends on module1.ts and by association module2.ts. Notice that the import is a little different from the previous one. Instead of importing everything from module1.ts and assigning a name for the module, we are only importing the MyClass1 class.

Next we wrap the MyApp class in a namespace just because this is what I would do in a real app to keep the global scope clean. If you're not familiar with it, namespace is the new keyword to create internal modules in TypeScript and is equivalent to the old keyword of module.

Inside the namespace we export the main application class, MyApp, so we can use it outside of the namespace. Inside the constructor is where we use the MyClass1 that we imported. We create a new instance of MyClass1, then call getText() on that object and output it to the web page.

The Script Tag


Now we need to set up our web page and get a module loader that can load the modules for us. At this time most browsers don't support the loading of modules using the ES6 specification. Therefore we are going to have to use something else. A well known module loading framework is RequireJS (http://requirejs.org/). So let's use that.

In order to use RequireJS we need to tell the TypeScript compiler which kind of package manager we are using. RequireJS implements the AMD standard so we must tell the TS compiler to use AMD. If you're in Visual Studio you can change it in the project properties. Otherwise change it in the tsconfig.json file ("module": "amd"). The TypeScript compiler will output the correct code that works with the package manager of your choice.

In the html file we only need one script tag that references the require.js file.

<script src="../Scripts/require.js" data-main="app"></script>


Notice that it has a data attribute called data-main. This is where you tell it what file is your entry point. In this case it's app.js, or just app (you don't need to specify the file extension). When the web page is loaded Require will load app.js first, then fulfill all of the imports specified inside the application's modules (module1.js and module2.js).

At this point the application is running and it is loading all the files separately. This great for our development environment. If all our files were packaged up in development it would make it a lot harder to debug.

Packaging the App


When it's time to release the app we need to package it up. For this we will be using a Node package called Webpack. You can easily install it using "npm install webpack -g". Then you can execute it from the command line telling it where your entry point file is and where to save the packaged file.

$ webpack ./src/app.js ./app.package.js


Here we tell it to start with app.js and save the result to app.package.js. Webpack will go through all of the imports just like Require did and load all the dependencies then save all of the code in the correct order in the output file along with any code it needs to bootstrap the app.

At this point you can change the require.js script tag to set data-main="app.package.js" and the application should run. Congrats, you now have a packaged up web application.

One thing to note about using Require, it's a very comprehensive library and therefore not a small file. The minified version is about 85kb. So you're making the user download 85kb just to run your app. If you don't need all of that functionality there is another, much smaller option, Require1k.

Require1k is a minimal library for loading CommonJS modules. If all you want to do is use modules in a web application it's perfect. And, as you can tell from the name, the file is only 1kb. That is a BIG difference. If you can use it instead you probably should.

To use Require1k we will need to change the TS compiler module type option to CommonJS ("module": "commonjs"). Other than that it works exactly the same. The script tag would look like this now:

<script src="require1k.js" data-main="./app"></script>


The only thing that changed was the name of the source file. From here we can use webpack just as we did before and everything should run, but with only 1k of bootstrap code.

Bonus Points: Minification


OK, all of our JS files are packaged up into one file. That's great but it could be better. We could minify the file to make it as small as possible. To do that we can use a Node package called Uglify. Install it using "npm install uglify-js -g".

It's very simple to use. Just point it at the file you want to minify.

$ uglifyjs ./app.package.js -o ./release/app.js -c -m


Use the -o option to specify where to save the minified file to. The -c option tells it to compress. The -m option tells it to mangle names which will rename everything it can to single letter names for more space savings.

Conclusion


Here we have seen how to use external modules in TypeScript/ES6 and how to use Require to load modules in the browser. Then we learned how to use the Node webpack tool to help us package up our TypeScript app for release while keeping them unpackaged for development. Then we seen how to use uglify to compress our application's code.