Getting my hands dirty with TypeScript

February 17, 2015 by PHP   TypeScript   Angular  

In my previous post I had a quick and dirty look at TypeScript, in this post I am going to reflect (mostly a collection of loose thoughts) on a little VS2013 TypeScript demo web application I wrote as an attempt to get into the groove of things. The accompanying download (containing the demo code) is available at the bottom of this post.

listdemo1


I used Bootstrap 3.3.1 for the UI layout, Angular 1.3.1 for data binding and managing the views and I included jQuery 2.1.3 (bootstrap is dependent on it). I also needed to nuget TypeScript definitions for these frameworks in order to use them properly in my TypeScript code.

At the time of writing this post, I couldn't find a type definition for Bootstrap 3, so I created my own rudimentary type definition that only exposes the functions I need (in this case only the modal plugin), observe:

interface JQuery {
    modal(command: string): JQuery;
}


This is rather interesting in that the JQuery interface is already previously defined in my imported type definition, but in TypeScript interfaces act like partial interfaces do in C#, so this action will merely extend the existing interface. I also created a little wrapper class for the Bootstrap modal plugin like seen below.

module Bootstrap {
    export class Modal {
        constructor(private selector: string) {
        }
        set Title(value: string) {
            $(this.selector + ' .modal-title').html(value);
        }
        Show() {
            $(this.selector).modal('show');
        }
        Hide() {
            $(this.selector).modal('hide');
        }
    }
}

The Bootstrap / Angular HTML5 markup, as seen in the abbreviated snippet below is fairly straightforward and mostly uninteresting, we've got a controller aka UserController (discussed later on in this post), a repeater that iterates through a list of users and a reference to an external html file that contains the modal markup used for adding / editing users to the list. Notice the external script file "main.js" however.

<!DOCTYPE html>
<html lang="en">
<head>
    ...
    <script src="main.js"></script>
</head>
<body role="document" ng-app="CSTruter">
	...
    <div class="container theme-showcase" role="main" ng-controller="UsersController">
        ...
                <table class="table table-striped">
                    ...
                    <tbody>
                        <tr ng-repeat="User in Users">
                            <td>...</td>
                            <td>{{User.FirstName}}</td>
                            <td>{{User.LastName}}</td>
                            <td>{{User.Username}}</td>
                        </tr>
                    </tbody>
                </table>
        ...
        <div ng-include src="'Views/Modals/User.html'"></div>
    </div>
</body>
</html>


When you examine the demo project, you will notice that I split my TypeScript files into logical parts, controllers, helpers, models etc. Which by default compiles into separate JavaScript files, it is obviously not prudent to include these files separately (from a http request round-trip perspective).

To remedy this situation there is a tickbox in the project properties under the the TypeScript Build tab called "Combine JavaScript output into file", this will compile all the TypeScript files into one JavaScript file aka main.js.

listdemo2


The following snippet will look very familiar to anyone that has worked with Angular before. This is essentially an Angular controller written in TypeScript. I am not going to go into too much detail about things like scope augmentation, but notice the type IUserScope assigned to the scope parameter.

class UsersController {
    private _activeUser: User = null;
    Users: User[] = [];
    constructor(private $scope: IUserScope, $http: ng.IHttpService) {
        var userModal = new Bootstrap.Modal('#userModal');
        var self = this;   
        $scope.Users = this.Users;
        $scope.showAddUserModal = function () {
            $scope.user = new User();
            userModal.Show();
            userModal.Title = 'Add User';
        }
        $scope.showEditUserModal = function (user: User) {
            userModal.Show();
            userModal.Title = 'Edit User';
            $scope.user = angular.copy(user);
            self._activeUser = user;
        }
        $scope.deleteUser = function (index : number) {
            $scope.Users.splice(index, 1);
        }
        $scope.saveUser = function () {
            if (self._activeUser != null) {
                angular.copy($scope.user, self._activeUser);
                self._activeUser = null;
            } else {
                $scope.Users.push($scope.user);
            }
            userModal.Hide();
        }
        Service.GetUsers($http, self.Users);
    }
}
CSTruter.controller('UsersController', UsersController);

This will constrain our scope to contain the members in the interface defined below, this might be a bit unnecessary, but nothing stops us to omit the type and leave the scope parameter to be open-ended if preferred (would have been nice if we could have had the best of both worlds - must contain x and y, but can add z dynamically).

interface IUserScope extends ng.IScope {
    Users: User[];
    user: User;
    showAddUserModal();
    showEditUserModal(user: User);
    deleteUser(index : number);
    saveUser();
}


The User array as seen in the interface above, gets populated with some dummy data via a web service, I know what you're thinking, you're using Web API right?

But for the purpose of demonstrating that we're not merely restricted to using Microsoft technologies when using TypeScript, I added a very simple PHP web service to this cesspool of a solution. I downloaded the rather impressive PHP tools for Visual Studio to keep it all together in one solution.

<?php 

header('Access-Control-Allow-Origin: *');  
header('Content-Type: application/json');

include 'Models/User.php';

$users = [
    new User('Bertie', 'Naude', 'bertie'),
    new User('Christoff', 'Truter', 'cstruter'),
    new User('Nelis', 'Van Schalkwyk', 'nelis'),
    new User('Pieter', 'Booysen', 'pieter')
];

echo json_encode($users);

?>


I think it is quite clear (in light of this demo) how easy it is to use existing JavaScript libraries in your TypeScript code, while leveraging the robustness that TypeScript brings. When used correctly this can greatly improve the way we maintain our client-side scripts, e.g If I decide to delete a property from my user model, or add a property, the compiler will warn you about these changes, instead of only becoming aware of them during runtime.


Leave a Comment




Related Downloads

TypeScript Angular List Demo