Jitender Sharma
Jitender Sharma

Reputation: 119

Angular JS - Convert Json Date and Apply Scope to model

I am creating a simple CRUD using AngularJS and facing a minor issue. While editing a form I am getting json string returned by server in model and I have created my custom directive to format that date. It formats the date but scope is not applied and when I submit the form I get the old json date in alert.

MY Js goes like this:

 var myApp = angular.module('myApp', ['toaster']).controller('MyController',function($scope){
$scope.old = {    ship_date : '/Date(1359743400000)/'    };

$scope.UpdateShipment = function ()     {
    alert($scope.old.ship_date);
}
}).directive('formatDate', function () {

return {
    require: 'ngModel',
    scope : {'ngModel' : '='},
    link: function (scope, element, attr, ngModelController) {

        ngModelController.$formatters.unshift(function (valueFromModel) {

            if (angular.isUndefined(valueFromModel)) {
                return valueFromModel;
            }

            var date = new Date(parseInt(valueFromModel.substr(6)));
            console.log(valueFromModel);
            return date.toLocaleDateString();
        });
    }
};
});

View:

 <div ng-app="myApp">
  <div ng-controller="MyController">
    <input type="text" ng-model="old.ship_date" />   <form ng-submit="UpdateShipment()">
     <input type="text"  format-Date ng-model="old.ship_date" />
     <input type="submit" id="submit" value="Save" />
   </form>
 </div>
</div>

PLEASE HELP , MANY THANKS.

Upvotes: 2

Views: 6455

Answers (6)

Mike Taber
Mike Taber

Reputation: 843

To build off of the answer from 'stride', you can also implement this using an interceptor. There's a few critical change you need to make when implementing it as an interceptor.

First, your .config section would look like this:

.config([
  $httpProvider", function($httpProvider) {
  $httpProvider.interceptors.push("httpInterceptorTransformResponseService");
  }
])

With that in place, you would build a factory service to intercept and transform the data similar to the example from http://aboutcode.net/2013/07/27/json-date-parsing-angularjs.html. The regex is somewhat arbitrary and depending on the data you're actually retrieving via your requests, you may want/need to change it.

(function () {
    "use strict";
    angular.module("yourAppNameHere")
    .factory("httpInterceptorTransformResponseService",
        [
            function () {
                // Purpose: This interceptor is intended to convert Json strings that match the ISO8601 date format  into DateTime objects.
                // This is necessary in many cases because there is not an object type embedded into serialized Json that represents a DateTime object.

                // There are numerous variations of this regex. Choose the one that works best for you, based on what data you expect
                var regexIso8601 = /^(\d{4}|\+\d{6})(?:-(\d{2})(?:-(\d{2})(?:T(\d{2}):(\d{2}):(\d{2})\.(\d{1,})(Z|([\-+])(\d{2}):(\d{2}))?)?)?)?$/;

                function convertDateStringsToDates(input) {
                    // Ignore things that aren't objects.
                    if (typeof input !== "object")
                        return;

                    for (var key in input) {
                        if (!input.hasOwnProperty(key))
                            continue;

                        var value = input[key];
                        var match;
                        // Check for string properties which look like dates.
                        if (typeof value === "string" && (match = value.match(regexIso8601))) {
                            var milliseconds = Date.parse(match[0])
                                if (!isNaN(milliseconds)) {
                                    input[key] = new Date(milliseconds);
                                }
                        } else if (typeof value === "object") {
                            // Recurse into object
                            convertDateStringsToDates(value);
                        }
                    }
                }

                // declare the service
                var transformResponseService = {
                    response: function (response) {
                        // convert all parsable date strings returned from the data into Date objects
                        convertDateStringsToDates(response.data);
                        return response;
                    }
                };

                return transformResponseService;
            }
        ]);
})();

CRITICAL PART: Notice near the end where the service is declared and the response for the transform is declared. The version above for the interceptor is called as follows:

convertDateStringsToDates(response.data);

and NOT

convertDateStringsToDates(response);

If you send response instead of response.data to the function when using an interceptor, what happens is that it will parse EVERYTHING that comes back from the entire http request. This means that in addition to the data it retrieves, it will also apply this to things like the http configuration, headers, etc.

I ran into an issue where it was running the regex against the config section of the http request. Because it's a recursive service and objects are treated like references, it entered an infinite loop and would overflow the stack. It's highly likely that you will only want to apply this to the data that's returned, not the configuration, headers, etc.

Upvotes: 0

stride
stride

Reputation: 1951

I had similiar issues, but i handled it in $http interceptor. You can transform the date strings into javascript objects. No need for custom directive. I think it is a lot cleaner approach.

Here is example implementation: http://aboutcode.net/2013/07/27/json-date-parsing-angularjs.html

  app.config(["$httpProvider", function ($httpProvider) {
     $httpProvider.defaults.transformResponse.push(function(responseData){
        convertDateStringsToDates(responseData);
        return responseData;
    });
}]);

Upvotes: 4

kfis
kfis

Reputation: 4729

just remove the scope-defintion from your directive

enter code here

http://jsfiddle.net/2vxCA/

Upvotes: 0

Jared Reeves
Jared Reeves

Reputation: 1390

All previous Answers about you needing to call $scope.apply() are correct, but as you have seen it can throw an error when another digest/apply is already in progress so to call it safely use this

if(!$scope.$$phase){$scope.$apply();}

this will only call it if it is not in progress and hopefully might fix your issue.

Upvotes: 1

Saravana Kumar
Saravana Kumar

Reputation: 836

You need to use $scope.$apply. Refer this post about modify scope inside directive.

Upvotes: 0

user2720708
user2720708

Reputation: 495

Use $scope.$apply() when your operating a function outside the angular way.

Try after the ngModelController({...}) function add $scope.$apply();

Upvotes: 0

Related Questions