Sunday, January 12, 2014

A video player with Angular

Back home its always the domestic projects that I sometimes find really fun and challenging to work on for the sheer reason lack of time. Also, you prove to the better half at home that they are important too.

As any kids do, my son spends most of time watching cartoons that are played out of an external flash drive or a laptop that should be connected to play on. Google Chrome seemed to make this job somewhat easy by playing it on your chrome browser and you need not let your flash drive or your laptop hook in physically. I just thought it will be call to play all the videos on the browser and chromecast it. That sounded great until I started to put in the video tag and sdtarted to play it. Unfortunately, I cannot play all the video's, we can play only one at a time and everytime video completes, I have to manually change/open a new file. I thought that's some place I can squeeze in some angular code and go blasting. Of course I do agree that there are a number of other ways you can do it, I tool to do this with Angular and it was easy enough. All that I needed was 2 things

videoplayer.html

 <!DOCTYPE html>  
 <html ng-app="videoplayer">  
 <head>  
      <script type="text/javascript" src="http://code.angularjs.org/1.2.0/angular.min.js"></script>  
      <script type="text/javascript" src="http://code.angularjs.org/1.2.0/angular-route.js"></script>  
      <script type="text/javascript" src="videoplayercontroller.js"></script>  
      <title>Check all orders you placed</title>  
 </head>  
 <body ng-controller="videoplayercontroller">  
      <div>  
           <fieldset>  
                <legend>List of files to be played</legend>  
                <ol>  
                     <li ng-repeat='file in mp4Files'> {{ sourceLocation }}{{ file }}</li>  
                </ol>  
           </fieldset>  
      </div>  
 <h2>{{ heading }}</h2>  
 <button ng-click="play()" class="button" ng-show="!playing">Play</button>  
 <button ng-click="pause()" class="button alert" ng-show="playing">Pause</button>  
 <button ng-click="stop()" class="button alert" ng-show="playing">Stop</button>  
 <br>  
 <video name="myvideoplayer" width='320' height='240' id='myvideoplayer'></video>  
 Playing video: <b>{{ playingFile }} - {{ playing }}</b>  
 </body>  
 </html>  

videoplayercontroller.js

 var app = angular.module('videoplayer', []);  
 app.controller('videoplayercontroller', ['$scope', function($scope) {  
      $scope.heading="Play music, all of them at once";  
      $scope.sourceLocation = './../Music/';  
      $scope.playingFile = '';  
      $scope.playing = false;  
      $scope.mp4Files = ['sample1.mp4', 'sample2.mp4'];  
      $scope.video = document.getElementById('myvideoplayer');  
  $scope.video.controls = true;  
      $scope.currentIndex = 0;  
  $scope.play = function() {  
   $scope.playing = true;  
   $scope.video.src = $scope.sourceLocation + $scope.mp4Files[$scope.currentIndex];  
   $scope.playingFile = $scope.video.src;  
   $scope.video.play();  
   $scope.video.webkitEnterFullscreen();  
  };  
  $scope.stop = function() {  
   $scope.video.pause();  
   $scope.playing = false;  
   alert('Stopped');  
  };  
  $scope.pause = function() {  
   $scope.video.pause();  
   $scope.playing = false;  
   alert('paused');  
  };  
  $scope.video.addEventListener('ended', function() {  
   $scope.$apply(function() {  
    $scope.currentIndex++;  
    if($scope.currentIndex >= $scope.mp4Files.length) {  
             $scope.currentIndex = 0;  
            }   
           $scope.play();  
   });  
  });  
 }]);  

Feel free to explore more on this or add additions to this sample, as always, your suggestions to improve this code is always welcome

Wednesday, January 1, 2014

Validations in AngularJS

There are different types of validation that we can do on Angular.
1. A simple form that has multiple control
2. A basic form that does not have many control in it, and that may need inline validation

Let's take a look at some common validation patterns on the form, like minimum, maximum length, required fields. The sample below should make it self evident. Run the sample code with the validations set, you will see a nice pop-over on every field if the validation fails. A lot of the manual effort has been taken out. Good.

<fieldset>
	<legend>Make your order</legend>
	<table>
		<tr>
			<td>Item</td>
			<td><input type="text" name="item" required ng-minlength=5 ng-model="purchaseItem.item"></td>
		</tr>
		<tr>
			<td>Quantity</td>
			<td><input type="text" name="quantity" required ng-model="purchaseItem.quantity" ng-minlength=1></td>
			<td><select name="measure" required ng-model="purchaseItem.measure">
				<option ng-repeat="quantity in quantities"> {{ quantity }}</option>
			</select></td>
		</tr>
		<tr>
			<td>From store</td>
			<td><input type="text" name="store" required ng-model="purchaseItem.store"></td>
		</tr>
		<tr>
			<td>Description</td>
			<td><textarea name="description" ng-maxlength=50 ng-model="purchaseItem.description"></textarea></td>
		</tr>
		<tr>
			<td><button ng-click="orderNewItem(purchaseItem)">Add</button></td>
			<td><button type="reset()">Clear/Cancel</button></td>
		</tr>
	</table>
</fieldset>

Next up is form submission. Again, the conventional way of doing things is to have a ng-submit tag with the method that needs to be invoked on it. I prefer associating methods on the submit method. Just my way of doing things, nothing against the conventional approach. We will see both the ways. Sample below should help our case

<tr>
	<td><button ng-click="orderNewItem(purchaseItem)">Add</button></td>
	<td><button type="reset()">Clear/Cancel</button></td>
</tr>

For the submit button to be evaluated from the controller, we may have to corresponding submit method. Here's the sample on that

addOrderController.js

function addOrderController($scope) {
	$scope.heading="Place your order here";
	$scope.scope="addorder";
	$scope.quantities=['Packets', 'lbs', 'Kg', 'ml', 'pieces', 'none'];
	$scope.purchaseItem = {};

	$scope.orderNewItem = function(purchaseItem) {
		console.log('Item to purchase is :' + purchaseItem.item);
	};

	$scope.reset = function() {
		$scope.purchaseItem = {};
	};
}

Here's the search form that has the other way for validation
<form name="searchForm" novalidate ng-submit="searchItem()">
		<fieldset>
			<legend>Search for an order</legend>
			<table>
				<tr>
					<td>Item</td>
					<td><input type="text" name="item" ng-model="searchItem.item" ng-maxlength=20></td>
					<td>From store</td>
					<td><input type="text" name="store" ng-model="searchItem.store" ng-maxlength=20></td>
					<td><button type="submit" ng-disabled="searchForm.$invalid">Search</button></td>
				</tr>
			</table>
		</fieldset>
		<div ng-show="searchForm.item.$dirty && searchForm.item.$invalid">
			<p>ERROR</p>
			<small ng-show="searchForm.item.$error.maxlength">Item name you are searching for should not be more than 20 characters long </small>
		</div>
		<div ng-show="searchForm.item.$dirty && searchForm.store.$invalid">
			<p>ERROR</p>
			<small ng-show="searchForm.store.$error.maxlength">Store name should not be more than 20 characters long </small>
		</div>
	</form>

Most of the attributes on HTML directives will make sense, but for "novalidate" in form element
"novalidate" prevents the browser from submitting the form

But this is not all, you can do a lot more. If your control needs  custom validation like pattern-matching, here's how you can do it

<input type="text" ng-pattern="/a-zA-Z/" />

We are so far good with client-side validation. How do you we get server-side validation done?
You can always do a $http request and on success you can set the success flag. We will do that in the examples to come down the line.

One other way you will come across in form validation is 'or'. Let's say you are building a search form where you have 2 fields and one of the 2 fields should be filled in for the submit button to be enabled. You can do one of these

<form name="searchForm" novalidate ng-submit="searchItem()">
	<fieldset>
		<legend>Search for an order</legend>
		<table>
			<tr>
				<td>Item</td>
				<td><input type="text" name="item" ng-model="searchItem.item" ng-maxlength=20 ng-required="!searchItem.store" ></td>
				<td>From store</td>
				<td><input type="text" name="store" ng-model="searchItem.store" ng-maxlength=20 ng-required="!searchItem.item"></td>
				<td><button type="submit" ng-disabled="searchForm.$invalid">Search</button></td>
			</tr>
		</table>
	</fieldset>
	<div ng-show="searchForm.item.$dirty && searchForm.item.$invalid">
		<small ng-show="searchForm.item.$error.maxlength">Item name you are searching for should not be more than 20 characters long </small>
	</div>
	<div ng-show="searchForm.item.$dirty && searchForm.store.$invalid">
		<small ng-show="searchForm.store.$error.maxlength">Store name should not be more than 20 characters long </small>
	</div>
</form>

Now, let's say we have another control and you are not interested in adding a lot of div tags around to deal with different types of errors. All you want is, allow JS to handle majority of the stuff. I see 2 reasons you want to do it.
1. Compilcated validations based on business
2. Want to re-use existing JS code in the previous application and this way you are confident that nothing is broken

If you are not in the above 2, I strongly encourage you to go the Angular way for validation
Anyway, back to what we were talking about. I added another control to my search form that does not have any of 'ng-*' valiudations on it. Instead, when the user taps on the submit button, JS kicks in does the validation and sets the error state for the new control. Then the typical Angular will detect that the form is invalid due to the field and displays the error message. Hope that is to your liking

searchOrderController.js

function searchOrderController($scope) {
	$scope.heading = "Search For your orders";
	$scope.scope = "searchorder";
	$scope.searchItem = {};
	$scope.submitted = false;
	$scope.errors = null;
	$scope.searchItem = function() {
		console.log('Search for item: ' + $scope.searchItem.item);
		console.log('Search for item bought from: ' + $scope.searchItem.store);
		console.log('Item purchased by: ' + $scope.searchItem.purchasedBy);
		if($scope.searchItem.purchasedBy == null) {
			$scope.searchForm.purchasedBy.$setValidity("server", false);
			$scope.searchItem.errors = "Purchased by should nto be EMPTY";
		} else {
			$scope.submitted = true;
		}
		console.log('processed');
	};
}

searchorder.html

<form name="searchForm" novalidate ng-submit="searchItem()">
	<fieldset>
		<legend>Search for an order</legend>
		<table>
			<tr>
				<td>Item</td>
				<td><input type="text" name="item" ng-model="searchItem.item" ng-maxlength=20 ng-required="!searchItem.store" ></td>
				<td>From store</td>
				<td><input type="text" name="store" ng-model="searchItem.store" ng-maxlength=20 ng-required="!searchItem.item"></td>
				<td>Purchased By</td>
				<td><input type="text" name="purchasedBy" ng-model="searchItem.purchasedBy" ng-maxlength=20></td>
				<td><button type="submit" ng-disabled="searchForm.$invalid">Search</button></td>

			</tr>
		</table>
	</fieldset>
	<div ng-show="searchForm.item.$dirty && searchForm.item.$invalid">
		<small ng-show="searchForm.item.$error.maxlength">Item name you are searching for should not be more than 20 characters long </small>
	</div>
	<div ng-show="searchForm.item.$dirty && searchForm.store.$invalid">
		<small ng-show="searchForm.store.$error.maxlength">Store name should not be more than 20 characters long </small>
	</div>
	<div ng-show="searchForm.purchasedBy.$invalid">
		<small>{{ searchItem.errors }}</small>
	</div>
</form>
Hope this helps....in our next samples we have to come up with some sort of template for the application. We have a lot of redundant code out there.

Routing in AngularJS

In the previous sample we saw a basic Angular page. Let's expand on that a bit to have a simple orders application.

Let's start with a simple HTML page, that has 3 links on it with the usual Angular stuff

index.html

<!DOCTYPE html>
<html ng-app="ordersApp">
<head>
<script type="text/javascript" src="http://code.angularjs.org/1.2.0/angular.min.js"></script>
<script type="text/javascript" src="http://code.angularjs.org/1.2.0/angular-route.js"></script>
<script type="text/javascript" src="ordersApp.js"></script>
<title>Make your orders here</title>
</head>
<body ng-controller="indexController">
<p>Current scope is {{ scope }} </p>
<div ng-show="{ scope=='index' }" ng-controller="indexController">
<h1>{{ heading }}</h1>
<h3>How it works?</h3>
<p>As soon as the item that needs to be purchased it added, item is registered in a CLOUD database that will in turn alert me/subscriber on their iPhone. It will also enable the end user to check for the item registerd and from where. If they have enabled location services, then when they reach the store, it will buzz on the items that need to be purchased from the store. Is that not fascinating enough for you?</p>
<br/>
<h3>What next?</h3>
<p>Once you purchase the item you should be provided with a facility to register the cost of the item. Next time when you purchase the same item from say a different store, you can the compare prices to check on which is the best and chepesr place for you to purchase the item. Did I say best....Yes, apparently I did. You will also be provided with a choice to select the quality of the item you purchased in the store. This will eventually enable you to ascerain the quality of the product. Not just the price alone. If that's not exciting in daily life, what is?</p>
<p>Now, let's dive in.....go on and give it a spin</p>
<div >
<table>
<tr>
<td>
{{ addOrderText }} <a href="addorder">here</a>
</td>
</tr>
<tr>
<td>
{{ listOrdersText }} <a href="listorders">here</a>
</td>
</tr>
<tr>
<td>
{{ searchOrderText }} <a href="searchorder">here</a>
</td>
</tr>
</table>
</div>
</div>
</body>

Here's the sample JS controller for the index page

indexController in orderApp.js

var ordersApp = angular.module('ordersApp', ['ngRoute']);

function indexController($scope) {
$scope.heading="A place to register all items that should be purchased";
$scope.addOrderText="Place you new orders";
$scope.listOrdersText = "Check all your orders";
$scope.searchOrderText="Search for an order";
$scope.scope="index";
}

Let's start by creating separate HTML files for each of the functionalities

addorder.html

<!DOCTYPE html>
<html ng-app="ordersApp">
<head>
<script type="text/javascript" src="http://code.angularjs.org/1.2.0/angular.min.js"></script>
<script type="text/javascript" src="http://code.angularjs.org/1.2.0/angular-route.js"></script>
<script type="text/javascript" src="ordersApp.js"></script>
<script type="text/javascript" src="addOrderController.js"></script>
<title>Place your order here</title>
</head>
<body ng-controller="addOrderController">
<h2>{{ heading }}</h2>
<div>
<p>Make sure the items you register here are clear and perceivable. If you are confused let me know, we could add a description/text field here to make it more easy for you.</p>
</div>
<div>
<table>
<tr>
<td><a href="index">Home</a></td>
<td><a href="listorders">List</a></td>
<td><a href="searchorder">Search</a></td>
</tr>
</table>

</div>
<br/>
<form name="orderForm">
<table>
<tr>
<td>Item</td>
<td><input type="text"></td>
</tr>
<tr>
<td>Quantity</td>
<td><input type="text"></td>
</tr>
<tr>
<td>From store</td>
<td><input type="text"></td>
</tr>
<tr>
<td><button type="submit">Add</button></td>
<td><button>Clear/Cancel</button></td>
</tr>
</table>
</form>
</body>
</html>

listorders.html

<!DOCTYPE html>
<html ng-app="ordersApp">
<head>
<script type="text/javascript" src="http://code.angularjs.org/1.2.0/angular.min.js"></script>
<script type="text/javascript" src="http://code.angularjs.org/1.2.0/angular-route.js"></script>
<script type="text/javascript" src="ordersApp.js"></script>
<script type="text/javascript" src="listOrdersController.js"></script>
<title>Check all orders you placed</title>
</head>
<body ng-controller="listOrdersController">
<div>
<p><h2>{{ heading }}</h2></p>
</div>
<div>
<table>
<tr>
<td><a href="index">Home</a></td>
<td><a href="listorders">List</a></td>
<td><a href="searchorder">Search</a></td>
</tr>
</table>

</div>
<br/>
<table>
<tr>
<th>Item</th>
<th>Quantity</th>
<th>Store</th>
<th>Operation</th>
</tr>
</table>
</body>
</html>

searchorder.html

<!DOCTYPE html>
<html ng-app="ordersApp">
<head>
<script type="text/javascript" src="http://code.angularjs.org/1.2.0/angular.min.js"></script>
<script type="text/javascript" src="http://code.angularjs.org/1.2.0/angular-route.js"></script>
<script type="text/javascript" src="ordersApp.js"></script>
<script type="text/javascript" src="searchOrderController.js"></script>
<title>Check all orders you placed</title>
</head>
<body ng-controller="searchOrderController">
<form name="searchForm">
<p><h2>{{ heading }}</h2></p>
<div>
<table>
<tr>
<td><a href="index">Home</a></td>
<td><a href="listorders">List</a></td>
<td><a href="searchorder">Search</a></td>
</tr>
</table>

</div>
<br/>
<table>
<tr>
<td>Item</td>
<td><input type="text"></td>
<td>From store</td>
<td><input type="text"></td>
<td><button type="submit">Search</button></td>
</tr>
</table>
</form>
<p>Results...</p>
<div>
<table>
<tr>
<th>Item</th>
<th>Quantity</th>
<th>Store</th>
<th>Operation</th>
</tr>
</table>
</div>
</body>
</html>

Note: All of the above HTML code are not perfect or even close to perfection. We will improve the code as we go along and of course our objective here is not HTML, but Angular JS. Feel free to chip in with your ideas and code improvements. I am forever open for that.

I prefer separate JS file for each controller and here's what I have in each of them

addOrderController.js

function addOrderController($scope) {
$scope.heading="Place your order here";
$scope.scope="addorder";
}

listOrdersController.js

function listOrdersController($scope) {
$scope.heading = "List of all item you ordered";
$scope.scope = "listorder";
}

searchOrderController.js

function searchOrderController($scope) {
$scope.heading = "Search For your orders";
$scope.scope = "searchorder";
}

Now that we have the basic HTML and the controller, our next step will be to introduce routing logic. To know how the routing works I suggest you read from here

Let's start with updating a router config in indexController.js. Pretty simple, when the user taps on addorder, we redirect to the respective HTML and assciate a controller for the HTML. Please make sure you tie the right controller with the HTML. If there is a mis-match on the controllers you will see some bizzare errors that are difficult to debug. Now you are all set with the routing mechanism

Updated orderApp.js

var ordersApp = angular.module('ordersApp', ['ngRoute']);

function indexController($scope) {
$scope.heading="A place to register all items that should be purchased";
$scope.addOrderText="Place you new orders";
$scope.listOrdersText = "Check all your orders";
$scope.searchOrderText="Search for an order";
$scope.scope="index";
}

ordersApp.config(['$routeProvider',
  function($routeProvider) {
  console.log($routeProvider);
    $routeProvider.
      when('/addorder', {
        templateUrl: './addorder.html',
        controller: 'addOrderController'
      }).
      when('/listorders', {
      templateUrl: './listorders.html',
      controller: 'listOrdersController'
      }).
      when('/searchorder', {
      templateUrl: './searchorder.html',
      controller: 'searchOrderController'
      }).
      otherwise({
        redirectTo: '/index.html'
      });
  }]);

Although this is one way of hadling different views, if you notice there is something big missing. What if we want all of the sub-views to be displayed in the index view itself, based on the selection choice. Sure we could do that too. In order to achieve that we have to make a few simple changes
our links in the index page should be local links, we should also add a ng-view in index.html and ensure you import the needed scripts to index.html. Here is the change for that. Try it out, and I am sure this will be a seed for a lot more things that you are/were thinking from the previous sample.

index.html for inline linking of views

<!DOCTYPE html>
<html ng-app="ordersApp">
<head>
<script type="text/javascript" src="http://code.angularjs.org/1.2.0/angular.min.js"></script>
<script type="text/javascript" src="http://code.angularjs.org/1.2.0/angular-route.js"></script>
<script type="text/javascript" src="ordersApp.js"></script>
<script type="text/javascript" src="addOrderController.js"></script>
<script type="text/javascript" src="listOrdersController.js"></script>
<script type="text/javascript" src="searchOrderController.js"></script>
<title>Make your orders here</title>
</head>
<body ng-controller="indexController">
<p>Current scope is {{ scope }} </p>
<div ng-show="{ scope=='index' }" ng-controller="indexController">
<h1>{{ heading }}</h1>
<h3>How it works?</h3>
<p>As soon as the item that needs to be purchased it added, item is registered in a CLOUD database that will in turn alert me/subscriber on their iPhone. It will also enable the end user to check for the item registerd and from where. If they have enabled location services, then when they reach the store, it will buzz on the items that need to be purchased from the store. Is that not fascinating enough for you?</p>
<br/>
<h3>What next?</h3>
<p>Once you purchase the item you should be provided with a facility to register the cost of the item. Next time when you purchase the same item from say a different store, you can the compare prices to check on which is the best and chepesr place for you to purchase the item. Did I say best....Yes, apparently I did. You will also be provided with a choice to select the quality of the item you purchased in the store. This will eventually enable you to ascerain the quality of the product. Not just the price alone. If that's not exciting in daily life, what is?</p>
<p>Now, let's dive in.....go on and give it a spin</p>
<div >
<table>
<tr>
<td>
{{ addOrderText }} <a href="#addorder">here</a>
</td>
</tr>
<tr>
<td>
{{ listOrdersText }} <a href="#listorders">here</a>
</td>
</tr>
<tr>
<td>
{{ searchOrderText }} <a href="#searchorder">here</a>
</td>
</tr>
</table>
</div>
</div>
<div ng-view></div>
</body>

Our next objective will be to handle a form that's been submitted, and do some basic validation on it. Thanks for you time. Don't forget to sign-off with you comments