Extending AnglarFire's (Firebase) $firebaseObject to allow for the storage of Dates.
New here? Learn about Bountify and follow @bountify to get notified of new bounties! x

Based on this question I posted on Stack Overflow, I want to extend AngularFire's $firebaseObject to allow for Firebase to store Javacript's native Date class.

The working code should contain three properties:

item.name: String

item.startDate: Date

item.endDate: Date

These three properties should be mapped directly to front-end inputs such as: <input ng-model="item.startDate" type="date"> So calling $save will save the three fields to Firebase.

To clarify, the transformation logic for the date should occur in the service—not in a middleman controller class. I'm trying to understand the correct, clean, and seamless way to handle the date class in Firebase.

Relevant code sample for extending the services is provided in the AngularFire documentation.

awarded to Nuno Freitas

Crowdsource coding tasks.

1 Solution

Winning solution

You can test the solution here: http://jsfiddle.net/5qL7gqsL/

Basically you extend $firebaseObject and:

  • in toJSON function convert the dates to a type that Firebase supports (in this case I convert dates to numbers using getTime)
  • in $$updated function convert back from Firebase storage to an actual Date object (in this case call the Date constructor with the value we previously extracted from getTime)

Source code below.

HTML:

<html ng-app="MyApp">

<head>
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.5/angular.min.js"></script>
    <script src="https://cdn.firebase.com/js/client/2.2.9/firebase.js"></script>
    <script src="https://cdn.firebase.com/libs/angularfire/1.1.2/angularfire.min.js"></script>
    <script src="app.js"></script>
    <style>
        input {
            display: block;
        }
        .form-group {
            margin-bottom: 15px;
        }
    </style>
</head>

<body ng-controller="MyCtrl">
    <form ng-submit="item.$save()">
        <div class="form-group">
            <label>Name</label>
            <input ng-model="item.name" type="text" placeholder="Name" />            
        </div>

        <div class="form-group">
            <label>Start</label>
            <input ng-model="item.startDate" type="date" />            
        </div>

        <div class="form-group">
            <label>End</label>
            <input ng-model="item.endDate" type="date" />            
        </div>

        <input type="submit" value="Save" />
    </form>

    JSON:
    <pre>{{item|json}}</pre>
</body>

</html>

Javascript (app.js):

/* global angular, Firebase */
var app = angular.module("MyApp", ["firebase"]);

app.controller('MyCtrl', function ($scope, Itinerary) {
    $scope.item = Itinerary('itinerary1');
});

app.factory('ItineraryFactory', function ($firebaseObject, $firebaseUtils) {
    return $firebaseObject.$extend({
        $$updated: function (snap) {
            var changed = $firebaseObject.prototype.$$updated.apply(this, arguments);

            if (changed) {
                // convert dates from Firebase storage
                this.startDate = new Date(this.startDate || 0);
                this.endDate = new Date(this.endDate || 0);
            }

            return changed;
        },

        toJSON: function () {
            return $firebaseUtils.toJSON(
                angular.extend({}, this, {
                    // convert dates to Firebase compatible type
                    startDate: this.startDate ? this.startDate.getTime() : null,
                    endDate: this.endDate ? this.endDate.getTime() : null
                })
            );
        }
    });
});

app.factory('Itinerary', function (ItineraryFactory) {
    return function (id) {
        var ref = new Firebase("https://nunof-sandbox.firebaseio.com/").child(id);

        return new ItineraryFactory(ref);
    };
});