Using AngularJS and TypeScript to Build Single Page Applications (SPA)

Posted by: Ravi Kiran , on 6/3/2014, in Category DNC Magazine
Views: 48406
Abstract: This article shows how to build Single Page Applications (SPA) using AngularJS and TypeScript and aims at providing a good understanding on implementing Angular components using TypeScript. You will create a sample video based training application which consumes its data using ASP.NET Web API

If you are a web developer active on the social media and have read about the latest web trends, then you don’t need an introduction to AngularJS. It is currently one of the hottest JavaScript frameworks for building Single Page Applications (SPA). AngularJS started catching the attention of many developers around the world because of its strong emphasis on code quality and testing and the ease with which one can get started with it and quickly build single page applications. AngularJS contains everything that one needs to build a full-fledged JavaScript based application, along with promoting good client-side coding practices.

This article is published from the DotNetCurry .NET Magazine – A Free High Quality Digital Magazine for .NET professionals published once every two months. Subscribe to this eMagazine for Free and get access to hundreds of free tutorials from experts

 

Getting Familiar with AngularJS

In the 10th edition of DNC magazine, Sumit Maitra posted an excellent article on building an app using Angular JS and ASP.NET MVC; you can take a look at it if you need an introduction to AngularJS.

Getting familiar with TypeScript:

 

TypeScript is a language created by Microsoft, which gets compiled into JavaScript. As the name itself suggests, TypeScript brings type checking capabilities into JavaScript. Since most of us are C# developers, TypeScript looks much like a combination of JavaScript and C#. TypeScript has most of the features of Object Oriented languages like C# and Java. The language designers didn’t try to invent anything, so one doesn’t need much time to get started with TypeScript.

TypeScript comes with a set of primitive types:

  • number: to represent numeric values
  • string: to represent string values
  • boolean: to represent Boolean values
  • any: to represent any type of value, to be used only when there are multiple possibilities

To create custom types, we need to define classes or interfaces depending on your applications need. Types can be inherited to create child types. Type inheritance in TypeScript is compiled into prototypal inheritance in JavaScript.

A simple TypeScript class looks like the following:

class Square {
  private sideLength: number;
  constructor(sideLength: number) {
    this.sideLength = sideLength;
  }

  public findArea(): number {
    return this.sideLength * this.sideLength;
  }
}

The compiled JavaScript of the above class is a self-executing function that returns a JavaScript constructor function, that takes one argument. The above class compiles to:

var Square = (function () {
    function Square(sideLength) {
        this.sideLength = sideLength;
    }
    
    Square.prototype.findArea = function () {
        return this.sideLength * this.sideLength;
    };
    return Square;
})();

As we saw, we need to specify types for every field and method in TypeScript. This may cause a question in your mind on possibility of using existing JavaScript libraries in TypeScript. Luckily, we don’t need to rewrite the libraries in TypeScript, we just need to have TypeScript declaration file for the library available. The type declaration file doesn’t define anything; it just holds declarations of all objects that the library exposes, of which most of them are interfaces.

Visual Studio 2013 provides rich editing support for TypeScript. The file gets compiled and corresponding JavaScript file is generated as soon as the source file is saved. We also get a nice split view to see the TypeScript and JavaScript equivalent side-by-side.

typescript-split-view

If you need some more inputs on TypeScript, check out Hello TypeScript – Getting Started

Building the OneStopTechVideos Sample App using AngularJS and TypeScript

Now that we got some background on TypeScript and AngularJS, let’s discuss about using TypeScript to write an AngularJS application. Let’s set up a project in Visual Studio 2013 to start creating the application. Open Visual Studio and create a new Empty Web Project named OneStopTechVids. As the name suggests, the application is used to maintain list of training programs offered by a Video-based virtual training company. To the project, add the following NuGet packages:

  • AngularJS.Core
  • AngularJS.Route
  • Bootstrap
  • AngularJS.TypeScript.DefinitelyTyped

The fourth package mentioned in the above list is the type declaration file for AngularJS.

Creating an ASP.NET Web API service to serve data

As the data resides on the server, let’s create an ASP.NET Web API service to serve data to the application. Since the focus of the article is to use AngularJS and TypeScript to build SPA’s, I am going to store the data in the memory in form of static collections. Let’s create two simple classes first: TechVideo and Category to represent structure of the data.

public class TechVideo
{
    public int Id { get; set; }
    public string Title { get; set; }
    public string Author { get; set; }
    public int Category { get; set; }
    public string Description { get; set; }
    public int Rating { get; set; }
}

public class Category
{
    public int Id { get; set; }
    public string Name { get; set; }
}

In a separate class, let’s fill in some sample data that has to be used in the SPA:

public class TechVideosData
{ 
    public static List< techvideo >TechVideos;
    public static List< category > Categories; 

    static TechVideosData()
    {
        Categories=new List< category >()
        {
            new Category(){Id=1, Name="JavaScript"},
            new Category(){Id=2,Name="ASP.NET"},
            new Category(){Id=3,Name="C#"},
            new Category(){Id=4,Name="HTML"},
            new Category(){Id=5,Name="CSS"},
            new Category(){Id=6,Name="Patterns 
            and Practices"}
        };

        TechVideos=new List< techvideo >()
        {
            New TechVideo(){Id=1, Title = "JavaScript 
            Patterns",Author="Ravi", Category=1, 
            Description="Takes a close look at most of the 
            common patterns in JavaScript", Rating=4},
            new TechVideo(){Id=2, Title = 
            "AngularJSFundamentals",Author="Suprotim", 
            Category=1, Description="Teaches basics of 
            Angular JS. Introduces the framework and 
            dives into the concepts around.", Rating=4},
            // More test data

        };
    }
}

We have two simple API services to perform operations on the above data. They are as follows:

public class CategoriesController : ApiController
{
	// GET api/< controller >
	public IEnumerable< category > Get()
	{
		return TechVideosData.Categories;
	}
}

public class TechVideosController : ApiController
{
	// GET api/< controller >
	public IHttpActionResult Get()
	{
		var videos = TechVideosData.TechVideos;
		return Ok(videos);
	}

	// GET api/< controller >/5
	public IHttpActionResult Get(string title)
	{
		var video = TechVideosData.TechVideos.FirstOrDefault(tv => tv.Title.Equals(title, StringComparison.InvariantCultureIgnoreCase));
		if (video != null)
		{
			return Ok(false);
		}
		else
		{
			return Ok(true);
		}
	}

	// POST api/< controller >
	public IHttpActionResult Post(
	[FromBody]
	TechVideo value)
	{
		var maxId = TechVideosData.TechVideos.Max(vid => vid.Id);
		value.Id = maxId + 1;
		TechVideosData.TechVideos.Add(value);
		return Ok(value);
	}

	// PUT api/< controller >/5
	public IHttpActionResult Put(int id, [FromBody]
	TechVideo value)
	{
		for (int counter = 0; counter < TechVideosData.TechVideos.Count; counter++)
		{
			if (TechVideosData.TechVideos[counter].Id == id)
			{
				TechVideosData.TechVideos[counter] = value;
				return Ok();
			}
		}

		return NotFound();
	}

	public IHttpActionResult Patch(int id, [FromBody]
	TechVideo value)
	{
		for (int counter = 0; counter < TechVideosData.TechVideos.Count; counter++)
		{
			if (TechVideosData.TechVideos[counter].Id == id)
			{
				TechVideosData.TechVideos[counter].Rating = value.Rating;
				return Ok();
			}
		}

		return NotFound();
	}

	// DELETE api/< controller >/5
	public IHttpActionResult Delete(int id)
	{
		for (int counter = 0; counter < TechVideosData.TechVideos.Count; counter++)
		{
			if (TechVideosData.TechVideos[counter].Id == id)
			{
				TechVideosData.TechVideos.RemoveAt(counter);
				return Ok();
			}
		}

		return NotFound();
	}
}

 

 

Building the Single Page Application (SPA)

Creating Angular module and configuring Routes:

The sample app that we are going to create in this walkthrough has three pages: List videos, Add a video and Edit a video. I am not going to cover the Edit video view here, but the code is made available in the downloadable file at the end of this article. I am leaving it to the readers to read the code and understand the functionality of the page.

To start with, add a new TypeScript file to the project and add references to Angular and Angular route to the file.

///<reference path="../scripts/typings/angularjs/angular.d.ts" /> ///<reference path="../scripts/typings/angularjs/angular-route.d.ts" />

Delete all the default code in the file and create a module. A module in TypeScript is similar to a namespace in C#. It can hold definitions of a number of types. While compiling to JavaScript as a function, you can also add some logic directly to the module, but amount of logic should be minimized.

module OneStopTechVidsApp { }

First thing that we add to any Angular JS application is creating a module and configuring routes. Statement for creating module remains the same in TypeScript as in plain JavaScript.

var app = angular.module("techVidsApp", ['ngRoute']);

Let’s try to understand the way a configuration block is created in JavaScript:

app.config(function (dependency) {
  //logic
});

The config block expects an executable function to be passed inside it. This function is going to be called immediately and it is not going to be instantiated. In the context of TypeScript, the function can be defined as one of the following:

  • A class with a constructor and with no other functional components
  • It can even be a class with a static function and the static function would be invoked while registering the config block

Let’s use the first approach to define the config. We will use the second approach later for another component. The config block needs $routeProvider for registering routes of the application. We can use the config.$inject to inject the dependency or, specify the dependencies in array format while registering the component. Following is our config block:

export class Config {
    constructor($routeProvider: ng.route.IRouteProvider) {
        $routeProvider.when("/list", {
            templateUrl: "App/Templates/VideoList.html",
            controller: "TechVidsListCtrl"
        })
            .when("/list/:id", {
            templateUrl: "App/Templates/VideoList.html",
            controller: "TechVidsListCtrl"
        })
            .when("/add", {
            templateUrl: "App/Templates/AddVideo.html",
            controller: "AddTechVideoCtrl"
        })
            .when("/edit/:id", {
            templateUrl: "App/Templates/EditVideo.html",
            controller: "EditTechVideoCtrl"
        })
            .otherwise({
            redirectTo: '/list'
        });
    }
}
Config.$inject = ['$routeProvider'];
app.config(Config);

Factory to interact with Web API:

Let’s create a factory with a number of asynchronous operations to talk to either Web API or to the local data cache. A factory is a function that returns an object. In general, JavaScript implementation of a factory looks like the following:

app.factory(‘name’, function (dependencies) {
    //Logic
    return {
        field1: value1,
        method1: function1,
        //Other members to be returned
    };
});

Simulating the exact same behaviour in TypeScript is a bit challenging. Let’s rephrase the factory as follows to make it easier to create it in the form of a class:

app.factory(‘name’, function (dependencies) {
    function FactoryType {
        this.field1 = < somevalue > ;
        this.method1 = function () {
            //Logic
        };
        //Other members of the FactoryType
    }
    return new FactoryType();
});

The object of the type FactoryType would look the same as the object returned in the first case; but it is created in a different way.

In the TypeScript class, we can create a static method that returns an object of the same class and registers the static method as a type. A skeleton of it looks like the following:

class MyClass {
    constructor() {
        //Logic of constructor
    }

    method1(): return -type { //Logic in the method
    };

    public static MyClassFactory() {
        return new MyClass();
    }
}

app.factory(‘myFactory’, MyClass.MyClassFactory);

Since our factory is going to talk to Web API endpoints, we need the dependencies of $http and $q in it. Also, as we will be dealing with custom objects for Video and Category, let’s create simple classes to hold the properties that we need in these objects. I prefer creating a separate module for creating the custom types, this keeps the declarations and the definitions separated. Following is a module with Video and Category classes:

module Extensions {

    export class Video {
        id: number;
        title: string;
        description: string;
        author: string;
        rating: number;
        category: number;
    }

    export class Category {
        id: number;
        name: string;
    }

}

In the factory class, we will have a set of private fields that have to be available for all the methods. We will set values to these fields in either constructor or in the methods. Following is the factory class with the required private fields, constructor and the static factory method:

export class TechVidsDataSvc {
    private videos: Array < Extensions.Video > ;
    private categories: Array < Extensions.Category > ;
    private techVidsApiPath: string;
    private categoriesApiPath: string;
    private httpService: ng.IHttpService;
    private qService: ng.IQService;

    constructor($http: ng.IHttpService, $q: ng.IQService) {
        this.techVidsApiPath = "api/techVideos";
        this.categoriesApiPath = "api/categories";

        this.httpService = $http;
        this.qService = $q;
    }

    public static TechVidsDataSvcFactory($http: ng.IHttpService,
    $q: ng.IQService): TechVidsDataSvc {
        return new TechVidsDataSvc($http, $q);
    }

}

All methods in the factory would be asynchronous and return promises. The private fields, videos and categories would be used to cache data locally and use them to serve data to the application whenever needed.

Let’s start adding logic to the factory by adding a method to fetch all videos. This method will make a request to the API service if the videos are not already loaded. Otherwise, it would return the cached data. In both cases, the data would be wrapped inside a promise to make behaviour of the method consistent. When it fetches data from the service, it updates the local cache with the data received. It is good to provide an option to forcefully refresh data from the server when needed; it can be done by accepting a Boolean value to this method. Following snippet shows the implementation:

getAllVideos(fetchFromService ? : boolean): ng.IPromise < any > {
    var self = this;

    if (fetchFromService) {
        return getVideosFromService();
    } else {
        if (self.videos !== undefined) {
            return
            self.qService.when(self.videos);
        } else {
            return getVideosFromService();
        }
    }

    function getVideosFromService(): ng.IPromise < any > {
        var deferred = self.qService.defer();

        self.httpService.get(self.techVidsApiPath).then(function (result: any) {
            self.videos = result.data;
            deferred.resolve(self.videos);
        }, function (error) {
            deferred.reject(error);
        });

        return deferred.promise;
    }
}

As you see, I created a function inside the method to avoid repeating the code to fetch videos from the API.

To add a video, we need to make an HTTP POST request. Once the API call is successful, the video object is added to the local cache after assigning id of the video received in response to the API call. This is done to keep the local copy consistent with the copy on the server. Following is the method that adds a video.

addVideo(video: Extensions.Video): ng.IPromise < any > {
    var self = this;
    var deferred = self.qService.defer();

    self.httpService.post(self.techVidsApiPath, video)
        .then(function (result) {
        video.id = result.data.id;
        self.videos.push(video);
        deferred.resolve();
    }, function (error) {
        deferred.reject(error);
    });

    return deferred.promise;
}

Other methods in the service follow a similar pattern. You can take a look at them in the source code.

Creating Main page and Menu

The page we are going to design has a menu on the left side with links to browse videos by categories or add a new video. At the centre, the page would contain the ng-view directive, the place where templates of other pages would be rendered.

angularjs-page-design

Following is the mark-up in the main page:


We need a small Controller to serve data about categories that are used to generate a list of links on the left menu. In Angular, a controller gets instantiated. So, we can simply create a class and register it as a controller, we don’t need to follow any conventions like we did in cases of the factory and the config blocks. But the objects used for data binding have to be made available on the Scope. A general interface type for scopes exists in the Angular type declaration file, but we need to create a child type to accommodate the new objects to be added to the scope specific to the controller.

Add the following interface to the Extensions module created above:

export interface ITechVidsCategoryScope extends ng.IScope {
   categories: Array;
}

The controller for serving the categories is straight forward. It just makes a call to the method in the service and assigns the obtained results to scope.

export class TechVidsCategoryCtrl {
    private $scope: Extensions.ITechVidsCategoryScope;
    private dataSvc: TechVidsDataSvc;

    private init(): void {
        var self = this;

        self.dataSvc.getAllCategories().then(function (data) {
            self.$scope.categories = data;
        });
    }

    constructor($scope: Extensions.ITechVidsCategoryScope,
    techVidsDataSvc: TechVidsDataSvc) {
        this.$scope = $scope;
        this.dataSvc = techVidsDataSvc;

        this.init();
    }
}

TechVidsCategoryCtrl.$inject = ['$scope', 'techVidsDataSvc'];
app.controller('TechVidsListCtrl', TechVidsListCtrl);

View to list all videos

Let’s design the View to list the videos. As you see in the above screenshot, the view consists of a list of divs that show details of the videos with an option to change rating of the video and a link for edit/delete. The voting buttons cannot be enabled for any value of rating. The buttons should prevent the user from going below 1 and going above 5. This can be achieved easily using Angular’s data binding. Following is the mark-up in the video list view:

{{video.title}}
by {{video.author}}
{{video.rating}}

This view is created to act in two ways: one is to display all videos and other is to display videos based on the category received in the URL. As we already saw, we need to create a scope type specific to this view.

export interface ITechVidsScope extends ng.IScope {
    videos: Array < Video > ;
    upRate(id: number, rating: number): void;
    downRate(id: number, rating: number): void;
}

The TechVidsListCtrl is responsible to fetch the videos based on route parameter and handle functionality to modify rating of the videos. Following is the implementation:

export class TechVidsListCtrl {
    private $scope: Extensions.ITechVidsScope;
    private $routeParams: Extensions.ITechVidsRouteParams;
    private dataSvc: TechVidsDataSvc;

    private init(): void {
        var self = this;

        //Fetching all videos if id is not found in route path
        if (self.$routeParams.id !== undefined) {
            self.dataSvc.getVideosByCategory(parseInt(this.$routeParams.id))
                .then(function (data) {
                self.$scope.videos = data;
            });
        }
        //Fetching videos specific to category if id is found in route path
        else {
            self.dataSvc.getAllVideos().
            then(function (data) {
                self.$scope.videos = data;
            });
        }
    }

    constructor($scope: Extensions.ITechVidsScope,
    $routeParams: Extensions.ITechVidsRouteParams,
    dataSvc: TechVidsDataSvc) {
        var self = this;

        self.$scope = $scope;
        self.$routeParams = $routeParams;
        self.dataSvc = dataSvc;

        self.$scope.upRate = function (id: number, rating: number) {
            self.dataSvc.setRating(id, rating + 1)
                .then(function () {
                self.init();
            });
        };

        self.$scope.downRate = function (id: number, rating: number) {
            self.dataSvc.setRating(id, rating - 1)
                .then(function () {
                self.init();
            });
        };

        self.init();
    }
}
TechVidsListCtrl.$inject = ['$scope', '$routeParams', 'techVidsDataSvc'];

View to add a new Video

The add video view is responsible to accept inputs, validate them and post the values to the Web API endpoint. We will use Angular’s data validation features to validate the inputs and display the error messages. The list of validations performed on this page is:

  • Video should have a title and the title shouldn’t be already assigned to any of the existing videos (custom validation directive, will be discussed later)
  • A category should have been selected
  • Author’s name should be entered and it shouldn’t have numbers or special characters
  • Video must have a description with at least 50 characters to 200 characters

Mark-up of the page seems heavy because of the validations. Following is the mark-up:


    




Please enter a title for the video Title already used
Please select a category
Please enter name of the author Author's name cannot contain numbers or special characters
Please enter a description for the video Description should have 50 - 200 characters

Following is the controller for this view:

export class AddVideoCtrl {
    $scope: Extensions.IAddTechVidScope;
    $window: ng.IWindowService;
    dataSvc: TechVidsDataSvc;

    constructor($scope: Extensions.IAddTechVidScope,
    $window: ng.IWindowService, dataSvc: TechVidsDataSvc) {
        var self = this;

        self.$scope = $scope;
        self.$window = $window;
        self.dataSvc = dataSvc;

        self.$scope.name = /^[a-zA-Z ]*$/;

        self.$scope.addVideo = function () {
            self.$scope.video.rating = 4;
            self.$scope.video.category = self.$scope.category.id;
            dataSvc.addVideo(self.$scope.video)
                .then(function () {
                var category = self.$scope.video.category;

                self.$scope.video = {
                    id: 0,
                    title: "",
                    description: "",
                    category: 0,
                    author: "",
                    rating: 0
                };
                self.$scope.techVidForm.$setPristine();
                self.$window.location.href = "#/list/" + category;
            });

        };

        self.$scope.cancelVideo = function () {
            self.$scope.video = newExtensions.Video();
            self.$scope.category = null;
            self.$scope.techVidForm.$setPristine();
        };

        self.init();
    }

    private init(): void {
        var self = this;

        self.dataSvc.getAllCategories()
            .then(function (data) {
            self.$scope.categories = data;
        });
    }
}
AddVideoCtrl.$inject = ['$scope', '$window', 'techVidsDataSvc'];

Directive to validate title

To check if the title assigned to the video is unique, we need to create a custom validation directive. In Angular, a directive is a function that returns a special object that has a set of pre-defined properties. In TypeScript, we can create a static method and make it return the required directive object.

For validation, we need two fields in the custom directive:

  • require, to be able to use functionalities of ngModel directive
  • link, to perform validation logic

The link function would invoke the $setValidity method of ngModel to set the validity of the input field based on certain condition. Following is the implementation of the directive:

export class UniqueVideoTitle {
    public static UniqueVideoTitleDirective(dataSvc: TechVidsDataSvc): ng.IDirective {
        return {
            require: 'ngModel',
            link: function (scope: ng.IScope,
            element: ng.IAugmentedJQuery,
            attrs: ng.IAttributes, ctrl: ng.INgModelController) {
                element.bind('blur', function () {
                    var viewValue = element.val();
                    dataSvc.checkIfVideoExists(viewValue)
                        .then(function (result) {
                        if (result === "true") {
                            ctrl.$setValidity("uniqueVideoTitle", true);
                        } else {
                            ctrl.$setValidity("uniqueVideoTitle", false);
                        }
                    }, function (error) {
                        ctrl.$setValidity("uniqueVideoTitle", false);
                    });
                });
            }
        };
    }
}
app.directive('uniqueVideoTitle', ['techVidsDataSvc', UniqueVideoTitle.UniqueVideoTitleDirective]);

Conclusion

I think, by now you must have got a good understanding on implementing Angular components using TypeScript. As we saw, TypeScript makes strict type checking on the objects and it doesn’t let the code compile unless it resolves all objects. This makes the life of programmers a lot easier as we don’t have to cross-check the properties referred on the objects that would have otherwise remained unknown till runtime.

Download the entire source code from our GitHub Repository at bit.ly/dncm12-angular

Give a +1 to this article if you think it was well written. Thanks!
Recommended Articles
Ravi Kiran is a developer working on Microsoft Technologies. These days, he spends his time on the front-end JavaScript framework Angular JS and server frameworks like ASP.NET Web API and SignalR. He actively writes what he learns on his blog at sravi-kiran.blogspot.com . He is a DZone MVB. You can follow him on twitter at @sravi_kiran


Page copy protected against web site content infringement by Copyscape


User Feedback
Comment posted by Yuri Seniev on Tuesday, June 3, 2014 12:03 PM
2 hours well spent doing Angular and Typescript
Comment posted by Artiom on Wednesday, June 4, 2014 2:17 PM
Please paste well formatted and highlighted code. Now it's really hard to read the post. Sometimes I skip such posts because of waste of time for parsing the code. Anyway thanks for the work
Comment posted by Brahm on Friday, June 6, 2014 12:01 AM
Seems to be nice article but bad formatting towed me away
Comment posted by Suprotim Agarwal on Saturday, June 7, 2014 1:39 AM
Sorry about the formatting. Hopefully by this month end, we will fix it.
Comment posted by Dale on Thursday, June 12, 2014 7:29 AM
Error   149   Build: The property 'id' does not exist on value of type '{}'.   C:\Users\Dale\Downloads\angularjs-with-typescript-dncmag-12-master (1)\angularjs-with-typescript-dncmag-12-master\OneStopTechVids\App\techVidsApp.ts   247   44   OneStopTechVids

Comment posted by Dale on Thursday, June 12, 2014 7:31 AM
Error   149   Build: The property 'id' does not exist on value of type '{}'.   C:\Users\Dale\Downloads\angularjs-with-typescript-dncmag-12-master (1)\angularjs-with-typescript-dncmag-12-master\OneStopTechVids\App\techVidsApp.ts   247   44   OneStopTechVids
Comment posted by Ravi on Tuesday, June 17, 2014 6:46 AM
Dale, Sorry for catching late. Are you getting this error when you build the sample from GitHub? I didn't have any issues in building and running it. Anyways, just check if you specified the correct data type to the video parameter in addVideo function. The data type should be Extensions.Video and the type should be containing all properties of Video.
Comment posted by mat on Wednesday, June 18, 2014 2:58 AM
The Error Comes when updating all NuGet Packages
Comment posted by Ravi on Wednesday, June 18, 2014 3:45 AM
What is the error you are getting while updating the NuGet package?
Comment posted by Ravi on Wednesday, June 18, 2014 3:45 AM
What is the error you are getting while updating the NuGet package?
Comment posted by Ravi on Wednesday, June 18, 2014 4:16 AM
What is the error you are getting while updating the NuGet package?
Comment posted by Suprotim Agarwal on Wednesday, June 18, 2014 9:18 AM
Fixed code formatting for this article.
Comment posted by selebs on Tuesday, June 24, 2014 4:24 PM
This is great, can you please have a video tutorial for it?
Thanks.
Comment posted by Thomas H Mitchell on Sunday, August 3, 2014 3:23 PM
There is no unit code test for the solution or project of any kind. It failed to build/run as of August 1, 2014 with current system development environment. You used a 0.9 Typescript build and the official release now is 1.0 Typescript which under your settings caused a fail. The compiler environment on your build was set to x86 and not any CPU.  The OneStopTechVid.ts file at code line #247 caused a build/run time failure.
Comment posted by Thomas H Mitchell on Sunday, August 3, 2014 3:47 PM
There is no unit code test for the solution or project of any kind. It failed to build/run as of August 1, 2014 with current system development environment. You used a 0.9 Typescript build and the official release now is 1.0 Typescript which under your settings caused a fail. The compiler environment on your build was set to x86 and not any CPU.  The OneStopTechVid.ts file at code line #247 caused a build/run time failure.
Comment posted by John Lee on Tuesday, August 5, 2014 4:16 PM
The compile error on line #247 in OneStopTechVid.ts happens after doing an update of all NuGet packages.  Also, before I did the update, I was getting the error 0x800a139e - JavaScript runtime error: [$rootScope:infdig] 10 $digest() iterations reached. Aborting!

This happens in IE 11 due to the following line:

$window.location.href = "#/list/" + self.$scope.video.category;

which is used in Add, Edit and Delete.  This seems to be a known issue with Angular and IE, but the question is whether there is a different way to do the redirect that will avoid this problem?
Comment posted by John Lee on Tuesday, August 5, 2014 4:42 PM
The compile error on line #247 in OneStopTechVid.ts happens after doing an update of all NuGet packages.  Also, before I did the update, I was getting the error 0x800a139e - JavaScript runtime error: [$rootScope:infdig] 10 $digest() iterations reached. Aborting!

This happens in IE 11 due to the following line:

$window.location.href = "#/list/" + self.$scope.video.category;

which is used in Add, Edit and Delete.  This seems to be a known issue with Angular and IE, but the question is whether there is a different way to do the redirect that will avoid this problem?
Comment posted by Ravi on Wednesday, August 6, 2014 1:33 AM
John,

Thanks for pointing the issue, I will try to find an answer.
Comment posted by Ravi on Saturday, August 16, 2014 12:49 AM
John,

Sorry for the delay in responding. IE has issues with URLs having # and when we directly replace the URLs using $window.location. But the issue doesn't appear when I changed $window.location statement to the following statement using $location:

$location.url("/list/" + self.$scope.video.category);
Comment posted by John Lee on Monday, August 18, 2014 8:27 PM
Thanks, Ravi.  That fixed the issue of IE bombing out after an add, save or delete.  Were you able to figure out the other issue?  The compile error on line #247 in OneStopTechVid.ts after updating NuGet packages?
Comment posted by Ravi on Tuesday, August 19, 2014 1:54 AM
John,

Initially, I couldn't get my head around it. After spending sometime again, I found the solution. At line #245, modify the post call as:

self.httpService.post<any>(self.techVidsApiPath, video)

When we don't specify the generic type with post method, it assumes the return type to be empty object {}, and hence doesn't find the property id at #247.
Comment posted by John Lee on Thursday, August 21, 2014 3:37 PM
Excellent, thanks for explaining this!  It's interesting that this only surfaced after the NuGet updates.
Comment posted by John Lee on Thursday, August 21, 2014 3:50 PM
It was the package angularjs.TypeScript.DefinitelyTyped that caused the build to fail.  Not surprising, I guess
Comment posted by Antelope Aeyoss on Monday, September 1, 2014 5:21 PM
Fixes to this code, for VS2013 Update 2RC are listed here:

http://ios-sharepoint.tumblr.com/post/96396660850/vs2013-update-2rc-debug-steps-in-test-of-spa
Comment posted by Ravi on Tuesday, September 2, 2014 11:46 AM
Antelope,
Thanks for posting solutions to the issues you faced. This will definitely help the readers.
Comment posted by Bijay on Tuesday, December 16, 2014 7:31 AM
Informative article Ravi..

Post your comment
Name:  
E-mail: (Will not be displayed)
Comment:
Insert Cancel