February 17, 2015 by Christoff Truter 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.
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'); } } }
<!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.
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);
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.