Saturday, October 15, 2016

AngularJS - formatter and parser example

When I work with an old project built use AxonIvy ,

I got an issue with date format

In that project, If I want to send a date from Web application using AngularJS to Server Side.

I need to send a date with format

For example:
{
 day: 31,
 month: 12,
 year:2016
}

Also the Server will return the same format for date fields.

For the client side, I use an AngluarJs UI Angular-Strap ,
Which provided very nice widgets.

Unfortunately, bsDate picker only work with common dateformat like ISO , UTC or Timestamp
which look likes:

"2016-10-16T04:06:15.481Z"

or 

1476590721627

In order to make Ivy Date work, I create a new directive :


app.directive('ivyDate', function() {
  return {
    require: 'ngModel',
    priority: 1,
    link: function(scope, elem, attrs, ngModelCtrl) {
      // formats text going to user (from model to view)
      ngModelCtrl.$formatters.push(function(value) {
        var modelValue = scope[attrs['ngModel']];
        return new Date(Date.UTC(modelValue.year, modelValue.month - 1, modelValue.day));
      });

      // formats text from the user (from view to model)
      ngModelCtrl.$parsers.push(function(value) {
        var date = new Date(value);
        return {
          day: date.getUTCDate(),
          year: date.getFullYear(),
          month: date.getMonth() + 1
        };
      });
    }
  };
})

So this directive with get date object from Server and display it as normal date to View

And everytime the View change, it will return a date with Server format to Model

One of the most important thing in my code is priority,

I set priority to 1 to ensure my $formatter function with run before the $formatter of bsDatePicks, After take my time to investigate that my formatter always run after the formatter of bsDatePicker, and bsDatePicker can only work with date in common format.

Here is remainng code:

Controller:


var app = angular.module('mgcrea.ngStrapDocs', ['ngAnimate', 'ngSanitize', 'mgcrea.ngStrap']);
app.controller('MainCtrl', function($scope, $timeout) {
  $scope.simpleDate= new Date();
  $scope.ivyDate= {
    day: 30,
    month: 11,
    year: 2016
  };
  $scope.setDate = function() {
    $scope.ivyDate= {
      day: 30,
      month: 11,
      year: 2015
    };
  }

HTML - View:

 <body ng-controller="MainCtrl">  
  <form name="datepickerForm" class="form-inline" role="form">  
   <div class="form-group" ng-class="{'has-error': datepickerForm.date2.$invalid}">  
    <label class="control-label"><i class="fa fa-calendar"></i> Normal Date <small></small></label>  
    <input type="text" class="form-control" ng-model="simpleDate" data-date-format="yyyy-MM-dd" data-date-type="number" data-min-date="02/10/86" data-autoclose="1" name="date2" bs-datepicker >  
   </div>  
   <hr />  
   <br />  
   <!-- Custom example -->  
   <div class="form-group" ng-class="{'has-error': datepickerForm.date2.$invalid}">  
    <label class="control-label"><i class="fa fa-calendar"></i> Ivy Date <small></small></label>  
    <input type="text" class="form-control" ng-model="ivyDate" data-date-format="yyyy-MM-dd" data-date-type="number" data-min-date="02/10/86" data-autoclose="1" name="date2" ivy-date bs-datepicker >  
   </div>  
   <br />  
   <br />  
   <button class="btn btn-primary" ng-click="setDate($event)"> setDate </button>  
   <textarea cols="14" rows="7">  
   {{ivyDate|json}}  
    </textarea>  
   <hr>  
  </form>  
 </body>  

Here is live demo on Plunker