Monday, February 8, 2016

Knockout with TypeScript - Embedded View Models

In my last post about Knockout and TypeScript I talked about interacting with observable arrays. This time we'll take a look at using embedded view models.

Knockout uses the concept of view models at its core. View models link to the view to provide data binding and functionality. There are times when you may want a more complicated structure for your view model. You may want to have child view models inside of view models so you can partition your application more effectively. So today I'll talk about how you do that.

What I sometimes like to do is create a master view model for my application in general, and then add view models underneath it for specific parts of the application. Say we have an application with two different views that have different data. In this example one view shows a list of favorite books and another shows favorite authors. You could have one view model with everything in it, but you can get a problem with namespacing if both views have similar properties.

class AuthorsViewModel1 {
    public appName = "Two views demo";
    public currentView = ko.observable("authors");
    public authors = ko.observableArray<string>();
    public books = ko.observableArray<string>();
    public sortAuthorsAscending = ko.observable(true);
    public sortBooksAscending = ko.observable(true);
    constructor() {
        this.sortAuthorsAscending.subscribe(() => this.authors.reverse());
        this.sortBooksAscending.subscribe(() => this.books.reverse());
    }
}

In this case we need to keep track of the sort order for both lists. Since they are both in the same view model we are required to give them unique names. We could make this easier by using child view models. Let's make a view model for authors and one for books.

class AuthorsViewModel {
    public authors = ko.observableArray<string>();
    public sortAscending = ko.observable(true);
    constructor() {
        this.sortAscending.subscribe(() => this.authors.reverse());
    }
}
class BooksViewModel {
    public books = ko.observableArray<string>();
    public sortAscending = ko.observable(true);
    constructor() {
        this.sortAscending.subscribe(() => this.books.reverse());
    }
}
class FavoritesViewModel {
    public appName = "Two Views Demo";
    public currentView = ko.observable("Authors");
    public authorsVM = new AuthorsViewModel();
    public booksVM = new BooksViewModel();
}

What we did was move the properties that were specific to each view into their own view model. Then we reference those view models inside of our main view model, FavoritesViewModel. This makes it a lot easier to determine what data goes with which view.

Now we need a way to use those view models in our HTML file. Knockout has a control flow binding that can help us out with that. It's the "with" binding. The "with" binding specifies a view model to use for a specific section of HTML.

<body>
    <h1 data-bind="text: appName"></h1>
    <select data-bind="value: currentView">
        <option>Authors</option>
        <option>Books</option>
    </select>
    <div data-bind="visible: currentView() === 'Authors', with: authorsVM">
        <h2>Favorite Authors</h2>
        <label><input type="checkbox" data-bind="checked: sortAscending" /> Ascending</label>
        <ul data-bind="foreach: authors">
            <li data-bind="text: $data"></li>
        </ul>
    </div>
    <div data-bind="visible: currentView() === 'Books', with: booksVM">
        <h2>Favorite Books</h2>
        <label><input type="checkbox" data-bind="checked: sortAscending" /> Ascending</label>
        <ul data-bind="foreach: books">
            <li data-bind="text: $data"></li>
        </ul>
    </div>
</body>

If you look at the <div> for our favorite authors section it has a "with: authorsVM" binding. From that point down in the DOM the context for the data will be the AuthorsViewModel object. So now if you're in the authorsVM context and you click the sort order checkbox it's going to refer to the sortAscending property on the authorsVM model. Same for the favorite books section, it has a "with: booksVM" binding.

The "with" binding in Knockout makes it easy for us to separate our view models by view which make it easier to read and maintain our code when dealing with multiple views. We start with a primary application view model and add child view models to it then associate them with different views.

TypeScript What/Why/Who/Where/When

Using TypeScript instead of JavaScript can really help to develop and maintain medium to large applications. In this post I'd like to go over some of the situations where I think TypeScript would be beneficial by answering the What/Why/Who/Where/When of TypeScript.

What

Let's start with "what" since you need to know what TypeScript is before we can start. TypeScript is a typed superset of JavaScript that compiles down to plain old JavaScript. What this means is that TypeScript is JavaScript with a few features added, such as static typing. This means that you can specify what type a variable or parameter to a function is. TypeScript has a number of built in types: string, number, boolean, array, enum, any, void. In addition you can create your own types using interfaces, classes and enums.The benefits of static typing are numerous and will be discussed in the "Why" section below.

In addition, TypeScript also has support for many of the new features in the ECMAScript 6 specification such as modules, classes and arrow functions (modules are like namespaces or packages). These will get compiled down JS that is compatible with lesser versions of JS so you can use them in older browsers.

Why

This is the most important question. Why use TypeScript at all. JavaScript is just fine, right?

Well, the problem with JS is that it's very loosely typed (you could argue that's also one of it's strengths). For example, you can create a variable and set it to a string, then in the next line of code set it to a number, then set it to an object. Variables don't have types in JS.

Same with function parameters. In all actuality function parameters are just a convenience in JS. You can pass as many or as few parameters as you like to a function and JS won't care (the inside of the function may care, but not JS).

Same with objects. A JS object is just an associative array, or hash table, or dictionary, or whatever you want to call it. JS objects have no definite shape.

While the absence of typing can sometimes be a very powerful tool, it can also cause a lot of confusion. When writing large applications, using types gives you piece of mind. Once you get to the point where you can't keep the entire application in your head, typing provides protection and knowledge.

First of all it's like having a set of built in unit tests that get run every time you compile. We can all agree that it's better to find problems earlier rather than later at runtime. Would you rather have the compiler tell you exactly where the problem is, or step through your code in the debugger?

Secondly, all you have to do is look at a function's parameters and their types to know exactly what the function expects. Without it you have to look at the function body to know what the function is expecting, or write very good comments. There's no need to write comments if you can look at a variable or parameter and see what its type is. Its self documenting.

Lastly, TypeScript lets your IDE give you better tooling support. It's very hard for an IDE to give you auto complete and auto suggest information when using an untyped language such as JS. For example, it has no idea what types the parameters to a function are, therefore it can't tell you for certain what it expects. Same with objects; it has no way of knowing what fields an object contains. But once your IDE knows about the types of variables and parameters it can give you all kinds of help. It's another form of documentation. You also get better refactoring and usage finding support.

TypeScript also has what are known as type definition files. They provide type information about external JS libraries. That means you get all the tooling support you would expect for your favorite JS libraries including jQuery, Angular, Lodash, etc. (there a TD files available for nearly every popular library out there). No more going to look up documentation because you forgot the signature of a function call in one of these libraries, it's right there at your fingertips, including the types that the function parameters expect.

Who

Now that we know what TypeScript is and why you should use it, let's answer the question of who TypeScript is for. Anyone can use TypeScript of course. But if you are a Java or C# programmer you will feel right at home with TypeScript. That's because TypeScript bridges the gap between what classical OOP programmers expect and how JS does prototypal inheritance. Behind the scenes TypeScript does all the work of converting classes and modules into JS objects.

I think one of the biggest problems that programmers coming over to JS from languages like Java or C# have is that they just don't understand JS objects and how prototypal inheritance works. When I first started writing JS that was my biggest hurdle. I spent a lot of time and energy trying to make JS objects follow the classical OOP paradigms I was used to. With TypeScript you don't have to worry about that because it supports classical OOP.

Even if you are a JavaScript programmer you might want to consider using TypeScript for larger sized projects just to get better tooling in your IDE. It's also going to make it easier in a team environment for other team members to read and understand your code. Remember, TS is JS so you don't have to use every feature in TypeScript all the time. Just use the pieces you want and ease into it. I recently worked on a project with a JavaScript programmer and convinced him to use TypeScript just to get better documentation from type definitions. By the end of the project he also became a fan of static typing and plans to use TypeScript from now on.

Where

You can use TypeScript anywhere that you use JS. That includes single page applications (SPAs) in the browser or Node.js applications. Since TypeScript compiles to plain JS it can be used in any browser that supports JS. You can even start using the ES6 features that TypeScript supports now rather than waiting until all of the browsers get enough support that you can specifically target ES6.

When

Should one use TypeScript all the time? I don't think so. Yes, you heard me right. I like writing TypeScript but there are cases when it's overkill, I'll be the first to admit that. My recommendation, and the rule of thumb I use, is when I can't keep everything in my head anymore then I switch to TypeScript. For me that's when the code starts to take up more than a page of code or I start breaking my code into multiple files. If I can't see it all then I have to spend time moving around looking for things which slows me down. It's also going to make it harder for someone else to read your code.

Remember the golden rule of programming: Code is written for people to read, not computers.

Tuesday, December 1, 2015

Knockout with TypeScript - Observable Arrays

In my first post about using Knockout with TypeScript I talked about the basics of using these two together. This time I want to look at using Knockout's observable arrays. Observable arrays allow you to track changes to arrays, like adding or removing elements. Then you can use a foreach binding in your markup to output the elements of the array.

A lot of times your application will have lists of items that will grow or shrink depending on user interaction. For this example we'll expand upon the first post and create a list of tasks. We'll create an observable array and then output it onto the page.

First let's define the Task class. It has task name, percent complete and is complete fields.

class Task {
    public taskName = ko.observable("");
    public pctComplete = ko.observable("0");
    public isComplete: KnockoutComputed<boolean>;
    constructor() {
        this.isComplete = ko.computed(() => this.pctComplete() === "100");
    }
}

Now let's take a look at the view model for our app. It has app name and tasks fields.

class AppViewModel {
    public appName = "Task List";
    public tasks = ko.observableArray<Task>();
}

Notice that the "tasks" field is defined as an observable array of Task objects. That means it is now being tracked by Knockout so whenever we add another task to the list it will update the page. The next thing we need is a way to add a task to the array. Let's create addTask and reset functions.

class AppViewModel {
    //...
    public addTask(): void
    {
        var task = new Task();
        task.taskName("Task " + this.tasks().length);
        this.tasks.push(task);
    }
    public reset(): void
    {
        this.tasks([]);
    }
}

In our addTask function we create a new Task object, set its name to the length of the tasks array, then add it to the end of the array using push(). Notice that to get the underlying array from a Knockout observable you have to execute the tasks function, e.g. tasks(). Just like any other observable it's a getter/setter.

We also added a reset function. This removes all of the tasks from the array by setting the tasks property to an empty array.

NOTE: There are some methods on the observable array object such as push() and pop(), which makes it seem like you're dealing with an actual array, but you're not. It is an object that wraps an array. This can get a little confusing at first, but you just need to remember that if you want to directly access the array you need to get it from the property first. Be careful there also! You must use the push() method of the observable object, not the underlying array, or it won't be observed by Knockout. My advice is don't access the underlying array unless you need something that the observable can't give you, like the length of the array.

Now let's go and write some markup that will display the task list on our page.

<body>
    <h1 data-bind="text: appName"></h1>
    <button data-bind="click: addTask">Add Task</button>
    <button data-bind="click: reset">Reset</button>
    <div data-bind="foreach: tasks">
        <div>
            <input type="text" data-bind="value: taskName" />
            % Comp:<input type="number" min="0" max="100" step="1" data-bind="value: pctComplete" />
            <input type="checkbox" data-bind="checked: isComplete" disabled />Completed
        </div>
    </div>
</body>

In our markup we define two buttons with click events. One calls the addTask() function in our view model and the other calls reset(). Next we define a div element with a foreach binding set to the tasks array. Now everything inside that div will be repeated for each Task object in the array.

Notice that when inside of a foreach we are in the context of the current element of the array. Therefore we can get the name of the current task by using "value: taskName" as well as the other properties of the Task object.

OK, we have everything we need so let's try it out. If we click the Add Task button a new task will appear on the page with the name "Task 0". If we click the button again another task will appear and so on.

Now change the percent complete of one of the tasks to 100. The completed checkbox will become checked because of our isComplete computed observable.


If you click the Reset button all of the tasks will be removed and the page updated.

Observable arrays in Knockout make it easy for us to create dynamic lists if elements on a page. With only a few lines of code we were able to create an interactive list of tasks. Just remember, an observable array object is a wrapper over a JavaScript array, not an array itself.

<jmg/>