James Fleming
James Fleming

Reputation: 2589

Date formatting issues with Knockout and syncing to breeze.js entityAspect modified

Ok, so this is not a first, but I'm having a hard time getting a date. ;-)

I'm using Breeze, Knockout. Have a form where I wish to show short date.

<input name="start" data-bind="value: start" class="date required" required="required" placeholder="mm/dd/yyyy" style=" width:142px"> 

yields a long dateTime: Wed Aug 31 2011 20:00:00 GMT-0400 (Eastern Daylight Time).

Creating a method to format the desired short date accomplishes the goal of creating a short date, but my modelContext is unaware of any change notifications. So my object won't notify the screen of changes. I can possibly kludge this by trying to notify the dataContext on click, etc, but I'm hoping to not have that lost during the conversion.

function positionInitializer(posit) {

    var shortDate = function (date) {
        return date && moment.utc(date).isValid() ? moment.utc(date).format('L') : "";
    };

    posit.start = ko.observable(shortDate(posit.start()));
}

Are there any decent examples on how to do this?

I don't think I can convert when I make my call for the query b/c I am expanding the number of tables in my call & you can't do both.

        var query = EntityQuery.from('Positions')
        .where('id', '==', id)
        .expand('Company, Projects')
        .orderBy(orderBy.positions);

Thought I'd see what the hive-mind thinks...

Upvotes: 6

Views: 13954

Answers (3)

Daniel
Daniel

Reputation: 270

If you're already using jQuery datepicker and you don't want to add another javascript library you could use:

ko.bindingHandlers.textDate = {
        update: function(element, valueAccessor, allBindingsAccessor, viewModel) {
            var value = valueAccessor(),
                allBindings = allBindingsAccessor(),
                valueUnwrapped = ko.utils.unwrapObservable(value),
                pattern = allBindings.datePattern || $.datepicker._defaults.dateFormat,
                valueFormatted = $.datepicker.formatDate(pattern, valueUnwrapped);

            $(element).text(valueFormatted);
        }
    };

However, it will only work with Date types.

Upvotes: 0

hubson bropa
hubson bropa

Reputation: 2770

@RyanRahlf Your answer gave me some inspiration so offering up configurable formatting and date validation add-ons to your solution.

My situation was a tad different. My date is coming in as JSON string (Ex. 2013-08-02T00:00:00) so I needed two formats, one from JSON, the other to what will be displayed (user friendly)

ko.bindingHandlers.date = {
    init: function (element, valueAccessor, allBindingsAccessor) {
        var formats = allBindingsAccessor().dateFormats || { from: "", to: "" };
        element.onchange = function () {
            var observable = valueAccessor();
            var value = moment(element.value)
            if (value && value.isValid()) {
                //if format is not set then assume observed is a js date
                if (formats.from) {
                    observable(value.format(formats.from));
                }
                else {
                    observable(value.toDate());
                }
            }
            else {
                observable("");
                //ensures element is blank when invalid input is attempted
                if (element.value) element.value = "";
            }
        };
    },
    update: function (element, valueAccessor, allBindingsAccessor) {
        var formats = allBindingsAccessor().dateFormats || { from: "", to: "MM/DD/YYYY" };
        var observable = valueAccessor();
        var valueUnwrapped = ko.utils.unwrapObservable(observable);
        if (valueUnwrapped) {
            element.value = moment(valueUnwrapped).format(formats.to);
        }
        else {
            element.value = "";
        }
    }
};

use (dateFormats optional with defaults)

<input type="text" data-bind="date: trueDate, dateFormats: { from: 'YYYY-MM-DDTHH:mm:ss', to: 'YYYY/MM/DD' }" />

Upvotes: 1

Ryan Rahlf
Ryan Rahlf

Reputation: 1812

There are a couple good options for handling date formatting using Knockout.

Writable Computed

You could create a writable computed for your date value and do all your formatting and parsing there. For example:

var myViewModel = function(){
    var self=this;
    self.trueDate = ko.observable(new Date());
    self.formattedDate = ko.computed({
        read: function(){
            return moment(self.trueDate()).format('L');
        },
        write: function(value){
            self.trueDate(moment(value).toDate());
        }
    });
}

<input type="text" data-bind="value: formattedDate" />

Any time the backing observable "trueDate" is updated its observers will be alerted.

Custom Binding

Another approach would be to build a custom data binding to format your data during binding and leave your view model simple.

var myViewModel = function(){
    var self=this;
    self.trueDate = ko.observable(new Date());
}

ko.bindingHandlers.dateString = {
    init : function(element, valueAccessor) {
        //attach an event handler to our dom element to handle user input
        element.onchange = function(){
            var value = valueAccessor();//get our observable
            //set our observable to the parsed date from the input
            value(moment(element.value).toDate());
        };
    },
    update: function (element, valueAccessor, allBindingsAccessor, viewModel) {
        var value = valueAccessor();
        var valueUnwrapped = ko.utils.unwrapObservable(value);
        if (valueUnwrapped) {
             element.value = moment(valueUnwrapped).format('L');
        }
    }
};

(Please keep in mind that the binding code above is untested, and doesn't check for invalid input, etc.)

And then your binding would be

<input type="text" data-bind="dateString : trueDate" />

I prefer the custom binding approach, since it can be easily reused for other dates and view models. A custom binding can also read other bindings on that element, so you could make the date format string configurable as a binding rather than hard-coding it to "L".

I hope this helps!

Upvotes: 7

Related Questions