Third-party library integration

One of the key components in large scale web development is the use of open source libraries that handle certain tasks for us. jQuery is one of the more popular libraries and is used for easy interaction with the Document Object Model (DOM). These libraries provide a wide variety of functionalities but all of these interactions can be given some level of typing. In this section, we are going to look at the following libraries:

  • jQuery
  • Knockout
  • RequireJS

Before we start developing these libraries, we need to get them referenced in our project. Each of these libraries can be downloaded directly from their web pages or through the use of NuGet. NuGet is a package manager for Visual Studio and can be installed as an extension.

Installing NuGet packages

The first thing we need to do is install the NuGet Package Manager. To do this, first select Tools from the menu and choose Extensions and Updates. This will bring you to a screen showing all of the extensions you currently have installed as well as provide you with the ability to find more online.

Select the Online section in the tree view on the left and search for NuGet Package Manager, as shown in the following screenshot:

Installing NuGet packages

Once you locate the package manager, download and install it. After this process is complete, Visual Studio will need to be restarted before we can start consuming packages.

Installing NuGet packages

The NuGet packages we will be installing will include the code for the libraries we will be using as well as separate declaration files for each of these libraries. Not every library has NuGet packages or declaration files associated with them, however, the web development community is very good about filling in these holes. There is a large repository of TypeScript library declaration files available from the DefinitelyTyped repository on GitHub.

Integrating with jQuery

jQuery is one of the most popular open source libraries available on the Web. It can be used for anything from DOM manipulation to simplifying remote server calls. If you plan on building a large scale web application you were probably already planning on using jQuery and if not, I strongly recommend that you do. To start using jQuery, the first thing we need to do is install the jQuery NuGet package. Right-click on the project and select Manage NuGet Packages. This will bring up a window similar to the updates and extensions window. Locate jQuery from the nuget.org repository in the Online section and install it. As you can see in the following screenshot, several files are installed with this package that help us develop with jQuery:

Integrating with jQuery

Now we can add a script tag the index.html file and jQuery will be available at runtime for us to use. This, however, isn't very useful during development and compilation of our TypeScript code because we don't have any type information about the library. To import this type information, we need a declaration file that represents the jQuery library. Open up the Manage NuGet Packages window again and search for jQuery.TypeScript. Install the package named jquery.TypeScript.DefinitelyTyped, which is on Version 1.3.5 at the time of writing this, to import the type information of the jQuery library.

Tip

There are a number of other libraries that extend jQuery and quite a few declaration files have been created to support these different extensions.

Now that the type information for jQuery has been included in the project we will be able to use the library in a more robust way. Let's add some elements to our HTML that we will manipulate with the jQuery library.

Integrating with jQuery

As you can see, we have added the script tag for jQuery to the document's head tag, and a couple of elements to the body. The first element is a large square div with a blue background. The second is a button that the user will click to hide the blue div tag that is shown. Now that we have the objects we want to manipulate, let's create some TypeScript to perform the expected operation.

In order to use jQuery in a strongly typed manner with the TypeScript compiler, we must include a reference to the declaration file at the top of the TypeScript file we wish to use it in. This is done using a specific format at the top of the TypeScript file; placing references anywhere but at the top of the page will cause them not to function. In the following example, you can see how to reference declaration files:

/// <reference path="scripts/typings/jquery/jquery.d.ts" />

The path included in the reference is relative to the file you are currently working in. Once this reference has been added, we are able to use IntelliSense and code completion to help us work with the jQuery API.

Integrating with jQuery

As you can see, we get the full feature set associated with Visual Studio's code completion.

Tip

When integrating with a large number of libraries it can be easier to manage your references by creating a _references.d.ts file and added references only to this file. Then, each of your code files only has to reference the single _references.d.ts file to import all of the relevant type information for the project.

As you can see in the following code sample, locating objects on the DOM and manipulating them is far easier with jQuery included and will reduce the amount of code we have to produce to make a dynamic application:

/// <reference path="scripts/typings/jquery/jquery.d.ts" />
$(document).ready(() => {
    $("#hideButton").click((event) => {
        $("#colorPanel").hide(1000);
    });
});

The first element in the file is the reference to the jQuery declaration file. This is what allows us to use the $ notation in our TypeScript without generating a compiler error, however, jQuery still needs to be loaded for this to work at runtime. We wait for the DOM to finish loading, which causes the function delegate to be called. At this time, we select the button we added to our HTML earlier on and listen for its click event to fire. When this occurs, another function delegate is called, which uses one of jQuery's UI modification methods called hide to make the large square div tag disappear over a period of 1000 milliseconds. Now if the application is run, there will be a large blue square just above the hide button. Once the button is clicked, you can watch the blue box disappear and the UI adjust for its disappearance. jQuery has a very large set of features and extensions. To learn more about this I recommend visiting http://jquery.com/ for an API reference and samples.

Integrating with Knockout

Software development in thick client applications, applications installed on the desktop, using technologies such as WPF have adopted new patterns to interact with the UI. Primary among these is the Model-View-ViewModel pattern, which aims to separate our business logic from the client application logic. Knockout is a JavaScript library that is meant to facilitate this pattern in the browser. You can install Knockout through the NuGet Package Manager as well as the declaration file for use in TypeScript. The packages used for this text are knockoutjs Version 3.1.0 and knockout.TypeScript.DefinitelyTyped Version 0.5.4. Knockout is dependent upon jQuery being available so make sure it is part of the project as well.

Knockout creates objects known as observables that other objects can subscribe to and will be notified if the value changes. When a value changes, the watchers can optionally take action on the new value of the observable. This comes in very handy when combined with Knockout's binding engine that allows us to bind our TypeScript/JavaScript objects to DOM elements. Let's take a look at an example of this binding. The first thing we need to do is create a ViewModel. This will be a class that contains observables and methods that we can bind to in the HTML of the application:

/// <reference path="scripts/typings/jquery/jquery.d.ts" />
/// <reference path="scripts/typings/knockout/knockout.d.ts" />
class ViewModel {
    public toggleText: KnockoutObservable<string> = ko.observable("Hide");
    public isVisible: KnockoutObservable<boolean> = ko.observable(true);
    constructor() {
    }
    public toggleClick(viewModel: ViewModel, event: JQueryEventObject) {
        if (this.isVisible()) {
            this.toggleText("Show");
            this.isVisible(false);
        } else {
            this.toggleText("Hide");
            this.isVisible(true);
        }
    }
}

As you can see, we create two observable objects providing the specific type as a type parameter using the generic syntax we discussed in Chapter 2, TypeScript Basics. The toggleText observable contains a string value that will be displayed on a button in our UI. The second observable holds a Boolean value that will determine whether an object in the DOM is visible to the user or not. Finally, we have a method that will be bound to a button click event and will change the value of these observables. This object will be bound to our HTML using the data-bind syntax defined by the Knockout API. The HTML code in the following screenshot shows our bindings:

Integrating with Knockout

As you can see, we include the jQuery and Knockout libraries in head. Then, in the body of the HTML, we have the two elements we created during the jQuery example. However, each of these elements now contains bindings. The colorPanel div is bound to the IsVisible observable using Knockout's visible binding. When the IsVisible observable changes, Knockout will evaluate the new value and manipulate the DOM to show or hide the div. The button is bound to multiple elements on the view model, the toggleText object, which will determine what the value of the button is, and the toggleClick method, which will execute anytime the click event fires.

Tip

Knockout supports many different kinds of bindings and also has a custom binding model that will allow you to define your own bindings. Visit www.knockoutjs.com to learn more.

OK, we have most of the pieces together now to dynamically bind the DOM to our TypeScript/JavaScript but we need to actually connect the view and the view model. The following code segment shows how to bind the two together:

$(document).ready(() => {
    ko.applyBindings(new ViewModel());
});

As you can see, we wait for the DOM to be ready before we apply the binding. If we attempt to bind before all of the elements are available an error will occur at runtime. Once the DOM is ready, we create a new instance of our ViewModel class and pass it to Knockout, which will handle the rest. Optionally, you can pass in a specific element to bind the view model to. Now when we run the application it starts with the large blue square visible and the text of the button is Hide. However, instead of this being the end of the application's life cycle as it was earlier, the button text has changed and we can perform another action, as shown in the following screenshot:

Integrating with Knockout

Using RequireJS

In Chapter 2, TypeScript Basics, we discussed module definitions but at the time we only focused on internal modules. At the time, external modules were mentioned but we did not explore them in-depth. Now that we are familiar with integrating external libraries we will bring in one of the more common external module libraries available. RequireJS is a JavaScript module loader used to ensure that all objects being used during application execution are available when necessary. This will allow us to separate our code into different files and not worry about the order in which they are loaded because RequireJS will manage that for us. Just like jQuery and Knockout, RequireJS and its declaration file are available through the NuGet Package Manager or can be found online at www.requirejs.org. The packages used for this text are "RequireJS" Version 2.1.14 and requires.TypeScript.DefinitelyTyped Version 0.2.0. For this next example, we will need all three libraries available. Let's put together a simple app that will allow us to add people to a directory and then search that directory.

RequireJS is an Asynchronous Module Loader (AMD) so we need to set the appropriate project settings. Open the TypeScript Build section of the project settings and set the Module system option to AMD. Now, we need to create a new module that will contain all of the code related to a set of types that represent people for the directory. Create a new TypeScript file called People.ts in the Scripts folder of the project. As you can see in the following sample, we define the IPerson abstraction and then implement it:

export interface IPerson {
    firstName: string;
    lastName: string;
    age: number;
}
export class Person implements IPerson {
    constructor(public firstName: string, public lastName: string, public age: number) {
    }
}

Both the IPerson interface and Person class are decorated with the export keyword, which will tell the compiler that these types are available in other modules that use this module. The resulting JavaScript is a little different from what was generated for internal modules:

define(["require", "exports"], function(require, exports) {
    (function (People) {
        var Person = (function () {
            function Person(firstName, lastName, age) {
                this.firstName = firstName;
                this.lastName = lastName;
                this.age = age;
            }
            return Person;
        })();
        People.Person = Person;
    })(exports.People || (exports.People = {}));
    var People = exports.People;
});

As you can see, the normal module definition is wrapped in another function called define that takes an array of dependent objects and a function to be called when all of the dependent modules are loaded. The next thing we need to create is a view model that we can bind our HTML to. We will need observables for the three input values for new people, the value that we want to search against, and an observable array to hold the search results. We will also need a method to initiate the addition process and a method to perform a directory search. We can place all of this code in a separate TypeScript file and use the import and require keywords to include the People module we just created:

import People = require("Scripts/People");
export class PersonFinderViewModel {
    public peopleArray: KnockoutObservableArray<People.IPerson> = ko.observableArray([]);
    public newFirstName: KnockoutObservable<string> = ko.observable("");
    public newLastName: KnockoutObservable<string> = ko.observable("");
    public newAge: KnockoutObservable<number> = ko.observable(null);
    public searchValue: KnockoutObservable<string> = ko.observable("");
    public searchResult: KnockoutObservableArray<People.IPerson> = ko.observableArray([]);
    constructor() {
    }
    public findPersonByFirstName(viewModel: PersonFinderViewModel, event: JQueryEventObject) {
        var people = this.peopleArray();
        var searchValue = this.searchValue();
        this.searchResult([]);
        for (var i = 0; i < people.length; i++) {
            if (searchValue === people[i].firstName) {
                this.searchResult.push(people[i]);
            }
        }
    }
    public addNewPerson(viewModel: PersonFinderViewModel, event: JQueryEventObject) {
        var newPerson = new People.Person(this.newFirstName(), this.newLastName(), this.newAge());
        this.peopleArray.push(newPerson);
        this.newFirstName("");
        this.newLastName("");
        this.newAge(null);
    }
}

As you can see, we provide the path to the People module to the require function, which tells RequireJS to add the module to the list of modules given to the define method, but also the TypeScript compiler for type information. We define a list of people that will be stored as the full directory list, the new people field observables, the search observables, and the two methods that will be bound to click events. The findPersonByFirstName method retrieves the most recent value of the searchValue observable as well as the list of people that we will be searching against.

Tip

If you are going to reference an observable or observable collection several times during a single execution block, it is faster to retrieve the value of the observable a single time, as shown in the findPersonByFirstName method. However, any modifications to the values should be made directly against the observables.

The existing set of search results is cleared to make way for new results. Finally, we iterate through the list of people and compare the search value to the first name of the current person. If there is a match, we add it to the list and continue on. The addNewPerson method retrieves all of the current values for the new person input values and passes them to the constructor of the Person class inside the People module. This person is then added to the observable array and the fields are cleared for a new entry.

Now that we have domain objects and a view model, we need to create the HTML to represent our application. Normally, we would have to add script tags for all of our JavaScript source files but since we are now using require we only need to add a few.

Using RequireJS

We include the references to jQuery and Knockout that will sit on the global namespace and then we have a script tag for require. Included in the tag for require is a binding called data-main that is used to define the entry point for the application.

Note

For more information regarding RequireJS please visit http://requirejs.org/.

In the body of the HTML, we have labels and inputs defining our UI and binding to each of the different objects in our view model. The search results section is doing something new that is worth noting. Using the foreach binding on the searchResult observable array, we have created a template for every item in the array. The context for these bindings is based on the type of the item being iterated over, in our case a Person object. While the firstName, lastName, and Age properties are not observables that can be bound to, the binding will only occur once. If the value of these fields change, the UI will not be updated. The last thing we need to do is create the entry point of our application. We will need to import the view model we created, wait for the DOM to be ready, and apply our bindings:

import PersonFinderViewModel = require("Scripts/PersonFinderViewModel");
$(document).ready(() => {
    ko.applyBindings(new PersonFinderViewModel.PersonFinderViewModel());
});

Now we can run the application and add several users to the directory. When we perform a search for someone by their first name, all of the matching results will be displayed:

Using RequireJS
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset