Обработка дат с помощью Asp.Net MVC и KnockoutJS

Недавно я начал работать с KnockoutJs и быстро понял, используя значение по умолчанию Json(myModelWithADate), в результате получилось стандартное json-кодирование \/Date(-62135578800000)\/. С небольшим количеством исследований я нашел четыре возможных способа обработки отображения моих дат в элементах dom.

1) Создайте привязку, которая обрабатывает преобразование с даты Json в желаемый формат

ko.bindingHandlers.date = {
    init: function (element, valueAccessor, allBindingsAccessor, viewModel) {
        var jsonDate = valueAccessor();
        var value = new Date(parseInt(jsonDate.substr(6)));
        var ret = value.getMonth() + 1 + "/" + value.getDate() + "/" + value.getFullYear();
        element.innerHTML = ret;
    },
    update: function(element, valueAccessor, allBindingsAccessor, viewModel) {

    }
};

Использование

<td data-bind="date: DueDate">
</td>

2) Верните "строки" из вашего контроллера

return Json(new {MyDate = DateTime.Now.ToShortDateString()});

3) Используйте JSON.NET, чтобы указать формат даты в формате james.newtonking.com

Пример

string isoJson = JsonConvert.SerializeObject(entry, new IsoDateTimeConverter());
// {"Details":"Application started.","LogDate":"2009-02-15T00:00:00Z"}

4) используйте JSON.parse для обработки ваших дат, как показано в этом qaru.site/info/36206/....

JSON.parse(jsonText, function(key, value) {
    // Check for the /Date(x)/ pattern
    var match = /\/Date\((\d+)\)\//.exec(value);
    if (match) {
        var date = new Date(+match[1]); // Convert the ticks to a Date object
        return humanReadable(date); // Format the date how you want it
    }

    // Not a date, so return the original value
    return value;
});

Они все работают, но я все еще борюсь, с которыми человек чувствует себя "правильно". Прямо сейчас моя кишка идет со смесью с привязкой и возвращающими строками. Поскольку я мог видеть, как я расширяю привязку для ввода ввода с помощью элементов управления дампинг jQuery UI.

Есть ли принятая практика при обработке отображения дат или других типов, таких как валюта? Есть ли другой вариант, который мне не хватает, который решает эту проблему?

Ответ 1

Лично я считаю, что решение JSON.NET является лучшим просто потому, что оно накладывает меньше на клиента. Все другие решения требуют дополнительного разбора клиента или дополнительного клиентского кода.

Я перешел на использование JSON.NET для всего моего кода ASP.NET, который использует JSON, потому что его гораздо более настраиваемая библиотека.

Например, мне пришлось реализовать данные JSON в MVC, которые соответствовали API Google Chart API (используется в сочетании с нокаутом для подкачки и т.д. ) и по умолчанию JavascriptSerializer просто не может этого сделать.

В дополнение к JSON.NET вы можете настроить его, чтобы на самом деле выплюнуть полные модели представлений Knockout, поэтому вам даже не нужно использовать плагин сопоставления.

Я написал образец библиотеки под названием FluentJson.NET, который позволяет делать вещи в Razor как:

var viewModel = @JsonObject.Create()
    .AddProperty("name", "value")
    .AddObservable("knockoutProperty", 123)

И получим:

var viewModel = {"name":"value","knockoutProperty":ko.observable(123)}

Таким образом, вы можете получить модель представления нокаута без каких-либо обходных кликов на стороне клиента.

Вы можете легко расширить что-то подобное, чтобы обрабатывать значения даты, но вы бы предпочли.

Ответ 2

Я предлагаю подход среднего человека через ko.mapping.fromJS( data, mapping ), это позволит вам настраивать даже с определенным пользователем объектом.

var $data = { _ID : '1', _Created : someDate };  
var $mapping = {
    '_Created' : {
       update: function (options) {
           return convertdata( options.data );
       }
    }
}
var viewDataModel = ko.mapping( data, mapping );  
ko.applyBindings( viewDataModel );

параметр сопоставления позволяет легко обрабатывать изменения и может легко использоваться с массивами.

Ответ 3

Лучший способ обработки дат в knockoutjs - использовать библиотеку времени и обрабатывать даты, такие как босс. Вы можете легко иметь дело с датами типа /Date (-62135578800000)/. Не нужно беспокоиться о том, как ваша дата сериализации в контроллере.

Подход 1: Непосредственно в поле зрения:

Допустим, что ваша модель нокаута получает такую ​​дату в наблюдаемом вызове sentDate, и теперь она имеет значение /Date (-62135578800000)/. Чтобы связать его в поле зрения, вы можете:

<p><label>Date</label>: <span data-bind="moment(sentDate).format('MM/DD/YYYY')"></span></p>

Подход 2: в пользовательской привязке

ko.bindingHandlers.date = {
    init: function (element, valueAccessor, allBindingsAccessor, viewModel) {
        var jsonDate = valueAccessor();     
        var ret = moment(jsonDate).format('MM/DD/YYYY');
        element.innerHTML = ret;
    },
    update: function(element, valueAccessor, allBindingsAccessor, viewModel) {

    }
};

Используется так же, как вы сказали:

<td data-bind="date: sentDate">
</td>

momentjs поддерживает множество дат и форматов функций в датах.

Ответ 4

Я использую следующий код для генерации коротких строк даты. Я использую его для строк даты и jQueryUi Date Picker.

class T
    {
        public DateTime d { get; set; }
    }

static void Main(string[] args)
    {
        var k = new T { d = DateTime.Now };

        var formatter = new IsoDateTimeConverter();
        formatter.DateTimeFormat = "d";
        var s = JsonConvert.SerializeObject(k, formatter);
    }

Это генерирует следующий JSON

"{"d":"4/21/2012"}"

Это приведет к чистому JavaScript-коду для меня.

Ответ 5

Просто пришел к этому вопросу, потому что мы также начали использовать knockout.js в нашем приложении MVC3. Поскольку у нас уже есть jQuery datepicker, и нам нужно форматировать даты по-разному по языку (портал имеет разные языки и разные форматы представлены на каждый язык), поэтому, возможно, этот гибрид технологических требований возникает где-то еще и будет полезен:

var jsDateFormat = "@CultureHelper.JsDateFormat"; // can be something like yy-mm-dd

//...

 ko.bindingHandlers.date = {
    init: function (element, valueAccessor, allBindingsAccessor, viewModel) {
        var value = valueAccessor();
        if (value != null) {
            var jsonDate = new Date(parseInt(valueAccessor().substr(6)));
            element.innerHTML = jQuery.datepicker.formatDate(jsDateFormat, jsonDate);
        }
    },
    update: function (element, valueAccessor, allBindingsAccessor, viewModel) {
    }
};

И в представлении тогда, например:

<p><label>Date</label>: <span data-bind="date: SentDate"></span></p>

Ответ 6

Более чистой альтернативой для ответа @photo_tom является украшение свойства с помощью IsoDateTimeConverter с помощью атрибута JsonConverter, например:

public class MyClass
{
    [JsonConverter(typeof(IsoDateTimeConverter))]
    public DateTime Timestamp { get; set; }
}

Ответ 7

Мне понравился ответ Андреса Торо, за исключением того, что в моем случае поля ввода ожидают отформатированные строки. Поэтому я использую JQuery для форматирования своих дат в соответствии с моим любимым форматом, предоставленным моим помощником @Html.ConvertDateFormat() Надеюсь, это поможет кому-то.

var mapping = {
    'ActualDateTime': {
        update: function (options) {
            var d = /\/Date\((\d*)\)\//.exec(options.data);
            return (d) ? $.datepicker.formatDate('@Html.ConvertDateFormat()', new Date(+d[1])) : value;
            //return "5/10/2017";
        }
    }
};
var paymentModel = ko.mapping.fromJS(modelJSON, mapping);

Ответ 8

Я всегда использую конвертер данных вместо того, чтобы отправлять данные непосредственно на сервер, чтобы исправить любые проблемы с кодировкой или синтаксическим анализом клиента, без необходимости использования дополнительных инструментов.

В файл модели представления Knockout JS я добавляю следующий код перед настройкой модели представления:

// converting data before sending to controller
var dataConverter = function (key, value) {  
    if (key === 'InvoiceDate') {
        return moment(value).format("YYYY MMMM DD");
    }

    return value;
};

Затем я использую dataConverter вместо data в методе ajax save в модели представления:

// Example view model for sales invoice
SalesInvoiceViewModel = function (data) {
    var self = this;
    ko.mapping.fromJS(data, {}, self);
    self.SaveInvoice = function () {
        $.ajax({
            url: '/SalesInvoices/SaveInvoice/',
            type: 'POST',
            data: ko.toJSON(self, **dataConverter**),
            contentType: 'application/json',
            success: function (data) {
                if (data.invoiceViewModel !== null) {
                    ko.mapping.fromJS(data.invoiceViewModel, {}, self);
                }
                if (data.redirectToLocation !== null) {
                    window.location = data.redirectToLocation;
                }
            },
            error: function (xhr, ajaxOptions, thrownError) {
                // report error to user
            }
        });
    }