Monday, 18 May 2015

Using When, All, Then and Reject functions of $q Service

We will see how to use when, all, then and reject functions of the AngularJS $q service. To learn the basic usage of $q, please see my previous post.

$q.when
$q.when() can be used to wrap an object or function if we are not sure whether it returns a promise or not. If we wrap a value object or a value returned function, the value will be passed as a result argument to the next function. When we wrap a function, the when() method will ensure that the function is executed first before going to the next one.

Below are some code examples:
$q.when('aaa').then(function(result) {
    alert(result);
})

var vm = this;
vm.chars = '';

$q.when(_test()).then(function(){
 vm.chars = vm.chars + 'END'
})

_test = function() {
 for (i = 0; i <= 10000; i++) {
   vm.chars = vm.chars + '.'
 }
}


$q.all
$q.all() is used to combine promise returned functions and only continue to progress further after all of the functions complete successfully. If one of the functions fails, immediately as soon as $q.all() gets a rejected promise from one of the functions, it will end the execution and pass forward to an error callback if there is any.
vm.numbers = '';    
_promiseFunction = function(x) {
 var deferred = $q.defer();

 if (x > 0) {
   deferred.resolve(x * 100);
 } else {
   deferred.reject(x + " is smaller than 0")
 }

 return deferred.promise;
}
 
var first = _promiseFunction(1);
var second = _promiseFunction(5);
var third = _promiseFunction(10);
var four = _promiseFunction(-1);
var five = _promiseFunction(-10)

$q.all([first, second, third /*, four, five*/]).
then(function(results){
  console.log(results.length);
  for(i=0; i < results.length; i++) {
    vm.numbers = vm.numbers + ' ' + results[i];
  }
 },
 function(errors){
  // if there is any error in the sequence, the success callback will not be called
  console.log(errors);
 })


$q.then
$q.then() is useful for executing asynchronous functions (or blocks of codes) sequentially in a chain. If the function inside a then() does not return a promise, it will be wrapped as a resolved promise. Otherwise the resolved or rejected promise will be passed forward.

_promiseFunction(5).then(
 function() { alert('aaa')}
).then(
 function() { alert('bbb');}
).then(
 function() {alert('ccc');}
);

_promiseFunction(5).then(
 function() { return $q.reject('failed')}
).then(
 function(){ alert('result success')},
 function(){ alert('result fail')}
)

Note also that if we return a non-promise value in the error callback inside then(), this will be treated as a resolved promise. So we need to be careful when handling error if our intention is to pass the error forward to the next function.
$q.reject().then(
 // success
 function() {},
 // error
 function(error) {
   return 'i got the error'
 }
).then(
 //success
 function() {
   alert('success');
 },
 // error
 function() {
   alert('error')
 }
)

$q.reject
Last thing, we have $q.reject() that is useful as a quick way to create then send a rejected promise (with a value optionally) to the next function in a chain. For example, in a chain of multiple then(). We can see this in the last two examples.

See the examples in Plunker.

Wednesday, 29 April 2015

AngularJS Custom Validator Example - Comparing Two Dates

Below is an example of AngularJS custom validator to compare an end date with a start date to make sure that the end date is equal or bigger than the start date. To see basic usage of AngularJS validation, please see my previous post.

HTML codes:
<!DOCTYPE html>
<html>

  <head>
    <script src="https://code.angularjs.org/1.4.0-beta.6/angular.js"></script>
    <script src="https://code.angularjs.org/1.3.15/angular-messages.js"></script>
    <script src="script.js"></script>
  </head>

  <body ng-app="validationExample">
    <div ng-controller="MyCtrl as vm">
      <form name="myForm" novalidate="" ng-submit="vm.submitted(myForm, vm.input)">
        <span class="input-label">Start Date</span> 
        <input type="date" name="startDate" ng-model="vm.input.startDate" required />
        <span ng-messages="myForm.startDate.$error" ng-messages-include="errors-template.html"></span>
        <br />       
        <span class="input-label">End Date</span> 
        
        <input type="date" name="endDate" ng-model="vm.input.endDate" start-date="{{vm.input.startDate.toDateString()}}" compare-with-start-date />
        <span ng-messages="myForm.endDate.$error" ng-messages-include="errors-template.html"></span>
        <br />             
        <button>submit</button>
      </form>
    </div>
    
    <!-- error messages template -->
    <script id="errors-template.html" type="text/ng-template">
        <span ng-message="required">* required!</span>
        <span class="form-error" ng-message="checkEndDate">* end date cannot be earlier than start date</span>
    </script>
  </body>

</html>

JavaScript codes:
var myApp = angular.module('validationExample', ['ngMessages'])

myApp.controller('MyCtrl', [function () {
    var vm = this;
    vm.submitted = function(form, input) {
      if(form.$valid) {
        alert('submitted');
      }
    }
} ]);

myApp.directive("compareWithStartDate", function () {
    return {
        restrict: "A",
        require: "?ngModel",
        link: function (scope, element, attributes, ngModel) {
            validateEndDate = function (endDate, startDate) {
                if (endDate && startDate) {
                    return endDate >= startDate;
                }
                else {
                    return true;
                }
            }

            // use $validators.validation_name to do the validation
            ngModel.$validators.checkEndDate = function (modelValue) {
                var startdate = Date.parse(attributes.startDate);
                var enddate = Date.parse(modelValue);
                return validateEndDate(enddate, startdate);
            };
            
            // use $observe if we need to keep an eye for changes on a passed value
            attributes.$observe('startDate', function (value) {
                var startdate = Date.parse(value);
                var enddate = Date.parse(ngModel.$viewValue);
                
                // use $setValidity method to determine the validation result 
                // the first parameter is the validation name, this name is the same in ng-message template as well
                // the second parameter sets the validity (true or false), we can pass a function returning a boolean
                ngModel.$setValidity("checkEndDate", validateEndDate(enddate, startdate));
            });
        }
    };
});

See the example in Plunker.

Thursday, 16 April 2015

Form Validation in Ionic or AngularJS

On this post we will see how to do form validation in AngularJS or Ionic framework. At the moment, Ionic framework does not have its own validation features and relies on the underlying framework.

Basic Usage with Built-In Validators
To begin, first we need to make sure that we have a reference to angular.js file. Then we need to do the following steps:
- give a name to our form
- put novalidate="" attribute on the form
So we should have something like this on our form:
<form name="myForm" novalidate="" ng-submit="" >
 . . .
</form>
- add a unique name to each input field
- add validation attributes that we need on the input fields. AngularJS provides some validation directives such as these; required, pattern, minlength, maxlength, min and max. It also has basic built-in validators for these input types; text, number, url, email and date.
An example on an input field:
<input type="text" name="name" ng-model="vm.input.name" required />
- add an error message to be displayed for each validator checking the corresponding property name on input field's $error object in conditional statement like ng-if or ng-show. Properties under $error object with the same names as the validators' names exist for us to use. The properties are on this format; formName.fieldName.$error.builtInValidatorName. For example:
<span ng-show="myForm.name.$error.required == true">* required</span>
- on a method that is called in ng-submit, check whether the form is valid using formName.$valid before doing any action

Below is a complete example:
<!-- HTML page -->
<body ng-app="validationExample">
    <div ng-controller="MyCtrl as vm">
      <form name="myForm" novalidate="" ng-submit="vm.submitted(myForm, vm.input)">
        <div>
          <input type="text" name="name" ng-model="vm.input.name" required>
          <span ng-show="myForm.name.$error.required == true">test: * required</span>
        </div>
        <button>submit</button>
      </form>
    </div>
</body>

// JavaScript codes
var myApp = angular.module('validationExample', [])

myApp.controller('MyCtrl', [function () {
    var vm = this;
    vm.submitted = function(form, input) {
      if(form.$valid) {
        // post the form
        . . .
      }
    }
} ]);
See these codes on Plunker.


Creating Custom Validator
If we would like to create a custom validator, we would need to do these in addition of the above steps:
- create a directive
- on the directive's link function, use the new AngularJS $validators object on ngModel controller to do the validation. We need to create a function property on ngModel.$validators. By doing so, we register the function as a new validation rule. Each time the model changes, the validation rule is invoked. For example:
link: function(scope, element, attributes, ngModel) {
 ngModel.$validators.wrongInitial = function(modelValue) {
   . . .
 };
}
A corresponding property on input field's $error object is also created that can be used for checking validity.
- similar like in using built-in validator, we need to add an error message to be displayed by checking the newly created corresponding property on the input field's $error object:
<span ng-show="myForm.name.$error.wrongInitial == true">* must start with letter r</span>

Additional codes to be added from the first example:
<!-- HTML page -->
<input type="text" name="name" ng-model="vm.input.name" start-with-r />
<span ng-show="myForm.name.$error.wrongInitial == true">* must start with letter r</span>

// JavaScript codes
myApp.directive("startWithR", function() {
    return {
      restrict: "A",
      require: "?ngModel",
      link: function(scope, element, attributes, ngModel) {
        ngModel.$validators.wrongInitial = function(modelValue) {
          if (modelValue) {
            return modelValue.charAt(0).toUpperCase() == 'R';
          }
          else {
            return true;
          }
        };
      }
    };
});
See a complete example on Plunker.


Using ngMessages to Display Error Messages
In the recent version of AngularJS, started with version 1.3, there is a new feature that we could use to display messages including form error messages. To use this, we need to:
- include a reference to angular-messages.js file in our page
- inject ngMessages module to our AngularJS app
var myApp = angular.module('validationExample', ['ngMessages'])
- if there are only a few fields to be validated, we could specify each error message for each field like this:
<!-- use ng-messages with each field's $error object in this format; formName.fieldName.$error -->
<div ng-messages="myForm.name.$error" >  
        <!-- specify an error message for each validator -->
 <span ng-message="required">Name is required!</span>
 <span ng-message="wrongInitial">Name must start with letter r</span>
</div>
- however if we know that we are going to repeat this multiple times, we could use a template instead:
<!-- create a template by using script tag with type="text/ng-template" and give an Id -->
<script id="errors-template.html" type="text/ng-template" >  
        <!-- specify an error message for each validator -->
 <span ng-message="required">* required!</span>
 <span ng-message="wrongInitial">* must start with letter r!</span>
</script>
Then use the template to display error message(s) on each field:
<!-- use ng-messages with each field's $error object and ng-messages-include referring to the template Id -->
<span ng-messages="myForm.name.$error" ng-messages-include="errors-template.html" />
See the complete example on Plunker.


References:
Working with Validators and Messages in AngularJS
Validation in Ionic Framework Apps with ngMessages

Friday, 27 March 2015

How to Pull File from an Android App

The other day when I tested my Apache Cordova based app, I tried to get a file from my Android phone to test that the file was created correctly by the app. I wanted to copy the file to a location somewhere in my Windows based desktop. Below are the steps of how I do this:
- open a command prompt
- enter 'adb shell', you might need to run this inside a folder that has adb.exe if that is not accessible globally
- enter 'run-as    your.application.name'
- enter 'chmod 777    targeted_File_Path' in order to give full access to the file
So after entering these commands, the command prompt would look like this:
C:\Users\Me>adb shell
shell@zara:/ $ run-as your.application.name
run-as your.application.name
shell@zara:/data/data/your.application.name $ chmod 777 /data/data/your.application.name/files/filename.jpg
data/data/your.application.name/files/filename.jpg                   <
shell@zara:/data/data/your.application.name $ 
- quit the adb shell or open a new command prompt window
- enter 'adb pull    targeted_File_Path    destination_Desktop_Folder'
On command prompt, it will be something like:
C:\Users\Me>adb pull /data/data/your.application.name/files/filename.jpg c:/DestinationDirectory
2097 KB/s (19335 bytes in 0.009s)


References:
http://stackoverflow.com/questions/13006315/how-to-access-data-data-folder-in-android-device
http://www.codeproject.com/Articles/825304/Accessing-internal-data-on-Android-device

Thursday, 19 March 2015

Example of Writing Image to File in Apache Cordova

Below is an example of how to writing image to a file in Apache Cordova. To have more understanding of basic file operations, please see my previous post.
window.resolveLocalFileSystemURL(my_Directory_Path,
function (dirEntry) {
 dirEntry.getFile(path_Of_File_To_Be_Written, { create: true }, 
  // getFile() success
  function (fileEntry) {
   fileEntry.createWriter(
    // createWriter() success
    function (fileWriter) {
     fileWriter.onwriteend = function (e) {
      . . .
     };

     fileWriter.onerror = function (e) {
      . . .
     };

     fileWriter.write(dataURIToBlob(getBase64Image(image_To_Be_Written, "image/jpeg")));
     // or if we already have a canvas
     //fileWriter.write(dataURIToBlob(canvas.toDataURL("image/jpeg")));
    }, 
    // createWriter() error
    function (error) {
     . . .
    }
   );
  },
  // getFile() error
  function (error) {
   . . .
  }
 );
});

A function to convert an image's base-64 encoded data to Blob type (this is taken from http://stackoverflow.com/questions/12391628/how-can-i-upload-an-embedded-image-with-javascript):
function dataURIToBlob(dataURI) {
 // serialize the base64/URLEncoded data
 var byteString;
 if (dataURI.split(',')[0].indexOf('base64') >= 0) {
  byteString = atob(dataURI.split(',')[1]);
 }
 else {
  byteString = unescape(dataURI.split(',')[1]);
 }

 // parse the mime type
 var mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0]

 // construct a Blob of the image data
 var array = [];
 for (var i = 0; i < byteString.length; i++) {
  array.push(byteString.charCodeAt(i));
 }
 return new Blob(
  [new Uint8Array(array)],
  { type: mimeString }
 );
}
Also a function to get base-64 encoded data from an image:
// type is either "image/png" or "image/jpeg"
function getBase64Image(img, type) {
    // create an empty canvas element
    var canvas = document.createElement("canvas");
    canvas.width = img.width;
    canvas.height = img.height;

    // copy the image contents to the canvas
    var ctx = canvas.getContext("2d");
    ctx.drawImage(img, 0, 0);

    // get the base-64 encoded data
    var dataURL = canvas.toDataURL(type);
 // toDataURL() actually has two optional parameters:
 // - type. The default value is 'image/png'.
 // - jpegQuality. A decimal value ranging from 0 to 1 to determine the quality of the data to be generated from a jpeg type image.
 //   The default value is browser dependant.

    return dataURL;
}

Monday, 16 March 2015

Examples of File Operations in Apache Cordova

To be able to do any operation with directory/file in Apache Cordova, we need to access and get hold of the directory/file entry first. Then only after that we will be able to do any directory/file operation. This could be a very different approach from other frameworks that we have used before in dealing with file and file system.


Creating and Writing File
Let start with an example to create and write a file:
window.resolveLocalFileSystemURL(cordova.file.dataDirectory, function (dirEntry) {
 console.log("got directory entry", dirEntry);
 dirEntry.getFile("myFilename.txt", { create: true }, 
  // getFile() success
  function (fileEntry) {
   console.log("got file entry", fileEntry);
   fileEntry.createWriter(
    // createWriter() success
    function (fileWriter) {
     fileWriter.onwriteend = function (e) {
      console.log('write is successful');
      . . .
     };

     fileWriter.onerror = function (e) {
      console.log('write is failed', e);
      . . .
     };

     var blob = new Blob(['. . . some text . . .'], { type: 'text/plain' });
     fileWriter.write(blob);
    },
    // createWriter() error
    function (error) {
     . . .
    }
   );
  },
  // getFile() error
  function (error) {
   . . .
  }
 );
});

In the example above, we use resolveLocalFileSystemURL() function to get a directory or file entry from a directory/file system path (i.e. cordova.file.dataDirectory). resolveLocalFileSystemURL() has two parameters:
- the first parameter is a directory or file path
- the second one is the callback function to be executed once it succeed. A directory or file reference entry object is passed to the callback function.

Once we have hold of the file or directory entry object, we can do a number of operations such as creating, writing, reading, copying, moving, renaming or deleting the file. In this case, we want to create a new file and write into it. To do this, we need to call getFile() function first. The function has four parameters:
- the filename or file path
- function options (optional parameter). It has two properties with boolean values, namely; create and exclusive. create: true will create a file if it does not exist. To make the function throws an error if the file exists, set exclusive: true.
- on success callback function, passing a file reference entry object
- on error callback function (optional parameter), an error object is passed to the callback
Then after we get the file entry object, we call createWriter() function that will create a file writer object once successful. Next we call its write() function. The file writer object also has onwriteend() and onerror() callback functions.


Reading File
window.resolveLocalFileSystemURL(myFilePath, function (dirEntry) {
 fileEntry.file(
  // success
  function(file) {
     var reader = new FileReader();

     reader.onloadend = function(e) {
       console.log('file is read');
       . . .
     };

     reader.readAsText(file);
  }, 
  // error
  function (error) {
    . . .
  }
 );
});
In the example, we see that after we get a file reference entry object, we use file() method in order to create a file object of the intended file to be passed to FileReader readAsText() function.


Deleting File
window.resolveLocalFileSystemURL(myFilePath, 
 function (fileEntry) { 
  fileEntry.remove(
   // success
   function () { 
    console.log('file is removed'); 
    . . .
   }, 
   // error
   function (error) {
    . . .
   }
  ); 
 } 
); 
In this last example, we call file entry object's remove() function to delete a file.

Thursday, 5 March 2015

Cropping Image with JavaScript and Canvas

Below is an example of how to crop image using JavaScript and HTML Canvas element.
var canvas = document.createElement('canvas');
canvas.width = 150;
canvas.height = 150;
//var canvas = document.getElementById('myCanvas');  // create a new canvas or use the existing one in document

var context = canvas.getContext('2d');

var sourceImg = new Image();

sourceImg.onload = function () {    // make sure the image is loaded first before cropping it

 context.drawImage(sourceImg, source_starting_x_coord, source_starting_y_coord, 
  width_to_be_cropped_from_source_coord, height_to_be_cropped_from_source_coord, 
  starting_x_coord_on_destination_canvas, starting_y_coord_on_destination_canvas, 
  width_of_cropped_image_to_put_on_canvas, heigth_of_cropped_image_to_put_on_canvas);
 //context.drawImage(sourceImg, 0, 50, 150, 150, 0, 0, 150, 150);

 // display or do anything we like with the result
 //imageDisplay = canvas.toDataURL();

}

sourceImg.src = toBeCroppedImageSource;  // can be an image url or base-64 encoded data