Jan
18
Written by:
Michael Washington
1/18/2016 1:37 PM
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.
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
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
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
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
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.
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
Now, just hit F5 and run the application.
The LightSwitch screen will show, with a link to go navigate to the AngularJs application.
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.
The Add button will allow you to create a new task.
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)
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)