Jan 18

Written by: Michael Washington
1/18/2016 1:37 PM  RssIcon

image

 

After watching Angular Applications with TypeScript, I decided to convert an earlier article I wrote called: Using JayData to Consume the Visual Studio LightSwitch OData Business Layer in a AngularJs CRUD Application using some of the things I learned.

The biggest thing I gained, besides the fact that, because it uses TypeScript, it provides compile-time checking, is that I can create a full CRUD application with a relatively small amount of code.

This project uses Visual Studio LightSwitch, so that I can provide an end-to-end solution that you can just download and run. However, the TypeScript /AngularJs / and JayData code will work against any back-end data source.

 

The Type Definition Files

This application uses AngularJs and you will want to get the TypeScript definition files: angular.d.ts and its required jquery.d.ts from DefinitelyTyped.

image

I created a TypeScript/Types directory in the Server project and put them in there.

JayData has a recommended method for using it with TypeScript but I decided to use my own method that allows me to use the Dynamic context initialization that does not require using the JaySvcUtil.exe.

To facilitate this, I created a JayDataApp.d.ts file, that I placed in the TypeScript/Types directory, that uses the following code:

 

declare module JayDataApplication {    
    export interface IJayData {
        initService: any;
        EntityState: any;
    }
}

 

This allows all the TypeScript code that I will add later to compile. Yes I could have provided stronger typing other than “any”, but it provides an example of how to use TypeScript when you have a JavaScript library that does not have a TypeScript definition file.

The View

image

I made some changes to the HTML mark-up from the original version. This new version uses the Controller As syntax.

This is the complete code:

 

<html>
<script src="http://code.jquery.com/jquery-2.0.3.min.js"></script>
<script src="http://code.angularjs.org/1.2.0-rc.3/angular.js"></script>
<script src="http://include.jaydata.org/datajs-1.0.3.js"></script>
<script src="http://include.jaydata.org/jaydata.js"></script>
<script src="http://include.jaydata.org/jaydatamodules/angular.js"></script>
<script src="TypeScript/JayDataApp.js"></script>
<script src="TypeScript/JayDataController.js"></script>
<body>
    <div data-ng-app="jayDataApp" ng-controller="JayDataController as vm">
        <ul>
            <li ng-repeat="ToDo in vm.colToDoes">
                <input id="checkbox" type="checkbox" ng-model="ToDo.IsComplete" disabled="disabled">
                <a href="#" ng-click="vm.selectedToDo = ToDo">{{ToDo.TaskName}}</a>
            </li>
        </ul>
        <button ng-click="vm.newToDo()">add new</button>
        <button ng-click="vm.refresh()">Refresh</button>
        <p>
            <form ng-if="vm.selectedToDo">
                <fieldset style="width: 300px; background-color: #FFFFCC;">
                    <legend>{{vm.selectedToDo.TaskName}}</legend>
                    <br />
                    <label>
                        <span><strong>Id:</strong></span>
                        <span>{{vm.selectedToDo.Id}}</span>
                        <span>
                            <br />
                            <strong>Task Name:</strong>
                        </span>
                        <input ng-model="vm.selectedToDo.TaskName" size="20" />
                        <span>
                            <br />
                            <strong>Is Complete:</strong>
                        </span>
                        <input type="checkbox" ng-model="vm.selectedToDo.IsComplete" />
                        <br />
                        <br />
                    </label>                    
                    <button ng-click="vm.save()">Save</button>
                    <button ng-click="vm.remove()">Remove</button>
                </fieldset>
            </form>
        </p>
    </div>
</body>
</html>

 

The Angular App

image

You will note that the HTML code (above) has a reference to a JayDataApp.js file that we have not created yet.

To create this, I created a JayDataApp.ts file with the following code:

 

module JayDataApplication {
    export class JayDataApp {
        static JayDataModule = angular.module('jayDataApp', ['jaydata']);
    }
}

 

This code creates a TypeScript class called JayDataApp that contains code to create an AngularJs module called jayDataApp. It also uses Dependency Injection to inject JayData into the application.

When the .ts file is compiled (automatically by Visual Studio), it will create the .js file that will run in the application.

The Controller

image

The last piece of code needed to complete the application is JayDataController.ts.

This is the complete code:

 

module JayDataApplication {
    export class JayDataControllerClass {
        // Constructor
        constructor(private $scope: ng.IScope, private $data: IJayData) {
            // Get the initial data
            this.GetApplicationData();
        }
        // Properties
        ApplicationData: any;
        colToDoes: Array<any> = new Array<any>();
        selectedToDo: any;
        // Methods
        GetApplicationData() {
            var vm = this;
            // Use JayData to call the OData service
            this.$data.initService('/ApplicationData.svc').then(function (objApplicationData) {
                vm.ApplicationData = objApplicationData;
                vm.ApplicationData.ToDoes.toLiveArray().then(function (ToDoes) {
                    ToDoes.forEach(objToDo => {
                        vm.colToDoes.push(objToDo);
                    });
                });
            });
        }
        refresh() {
            // Clear existing colToDoes and set as a local value
            // so it can be used inside the callback function
            var colToDoes = this.colToDoes = new Array<any>();
            // Refresh the data
            this.ApplicationData.ToDoes.toLiveArray().then(function (ToDoes) {
                ToDoes.forEach(objToDo => {
                    colToDoes.push(objToDo);
                });
            });
        }
        newToDo() {
            var ctx = this.ApplicationData;
            // Add a new ToDo item
            this.selectedToDo = new ctx.ToDoes.elementType({
                // Set the default value for the Task Name
                TaskName: "[New Task]"
            });
        }
        remove() {
            // Remove the ToDo item
            this.ApplicationData.ToDoes.remove(this.selectedToDo);
            this.saveChanges();
        }
        save() {
            if (this.selectedToDo.Id) {
                // Save an existing ToDo item
                this.ApplicationData.ToDoes.attach(this.selectedToDo, true);
                this.selectedToDo.entityState = this.$data.EntityState.Modified;
            }
            else {
                // Save a new ToDo item
                this.ApplicationData.ToDoes.add(this.selectedToDo, true);
            }
            this.saveChanges();
        }
        saveChanges() {
            // Save any changes 
            var vm = this;
            this.ApplicationData.saveChanges().then(function () {
                vm.selectedToDo = null;
                vm.refresh();
            }, function (error) {
                // Get the validation error messages from LightSwitch
                var xml = error.message,
                    xmlDoc = $.parseXML(xml),
                    $xml = $(xmlDoc),
                    $ValidationResults = $xml.find("ValidationResults");
                if ($ValidationResults.length == 0) {
                    // This is a simple error
                    var $MessageError = $xml.find("Message")[0].childNodes['0'].data;
                    alert($MessageError);
                } else {
                    // This is a entity validation error
                    angular.forEach($ValidationResults, function (ValidationResult) {
                        angular.forEach(ValidationResult.childNodes, function (childNode) {
                            alert(childNode.childNodes[0].textContent);
                        });
                    });
                }
                vm.ApplicationData.stateManager.reset();
            });
        }
    }
    // In Angular this is actually the first thing to run
    // It is at the bottom of the TypeScript class so that the JayDataControllerClass
    // can be created before the AngularJs controller is created
    // $scope = AngularJs scope
    // $data = JayData functionality
    // JayDataControllerClass = a class (above) that becomes a JavaScript function 
    JayDataApp.JayDataModule
        .controller("JayDataController", ["$scope", "$data", JayDataControllerClass]);
}

 

Include The .js files created by TypeScript

image

TypeScript will create .js files for the .ts files (but not the d.ts files). However, they will not be included in the project unless you add them.

Click anywhere in the Server project and select Show All Files.

image

Click on the .js and .js.map files (the .map files are used to allow you to debug the .ts files at run-time), and right-click on them and select Include In Project.

 

Running The Application

image

Now, just hit F5 and run the application.

The LightSwitch screen will show, with a link to go navigate to the AngularJs application.

image

When you navigate to the AngularJs application, any existing entries will display.

clicking on an existing task will display it in a form to allow editing.

image

The Add button will allow you to create a new task.

image

Note that server-side validation is enforced.

(see Using JayData to Consume the Visual Studio LightSwitch OData Business Layer in a AngularJs CRUD Application for more details on this)

image

Clicking Save will insert or update an existing task.

Remove will delete a task.

 

Links

TypeScrip.org

DefinitelyTyped

Angular Applications with TypeScript

Using JayData to Consume the Visual Studio LightSwitch OData Business Layer in a AngularJs CRUD Application 

 

Download

The LightSwitch project is available at http://lightswitchhelpwebsite.com/Downloads.aspx

You must have Visual Studio 2015 (or higher) with LightSwitch installed to run the code (if you have Visual Studio Community Edition see How To Get Visual Studio LightSwitch For Free)


Your name:
Gravatar Preview
Your email:
(Optional) Email used only to show Gravatar.
Your website:
Title:
Comment:
Security Code
CAPTCHA image
Enter the code shown above in the box below
Add Comment   Cancel 
Microsoft Visual Studio is a registered trademark of Microsoft Corporation / LightSwitch is a registered trademark of Microsoft Corporation