web-dev-qa-db-fra.com

Gestion des dates avec Asp.Net MVC et KnockoutJS

J'ai récemment commencé à travailler avec KnockoutJs et je me suis vite rendu compte que l'utilisation de Json(myModelWithADate) par défaut entraînait le codage json par défaut de \/Date(-62135578800000)\/. Après quelques recherches, j'ai trouvé quatre façons possibles de gérer l'affichage de mes dates dans des éléments dom.

1) Créez une liaison qui gère la conversion de la date Json au format souhaité

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) {

    }
};

Usage

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

2) Renvoie les «chaînes» de votre contrôleur

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

3) Utilisez JSON.NET pour spécifier un format de date/heure vu sur james.newtonking.com

Exemple

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

4) utilisez JSON.parse pour gérer vos dates, comme indiqué dans cette réponse stackoverflow.

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;
});

Ils semblent tous fonctionner, mais je lutte toujours pour ce qui est «juste». En ce moment, mon intestin est mélangé avec les chaînes de reliure et de renvoi. Comme je pouvais me voir étendre la liaison pour gérer les entrées avec les contrôles datepicker de l'interface utilisateur jQuery. 

Existe-t-il une pratique acceptée lors du traitement de l'affichage de dates ou d'autres types tels que la devise? Y at-il une autre option qui me manque qui résout ce problème?

30
Mark Coleman

Personnellement, je pense que la solution JSON.NET est la meilleure simplement parce qu’elle impose moins au client. Toutes les autres solutions nécessitent une analyse syntaxique ou un code client supplémentaire.

Je suis passé à JSON.NET pour tout mon code ASP .NET qui utilise JSON, car il s'agit d'une bibliothèque beaucoup plus personnalisable. 

Par exemple, j'ai dû implémenter dans MVC des données JSON conformes à l'API graphique de Google (utilisé en combinaison avec Knockout pour la pagination, etc.) et la valeur par défaut JavascriptSerializer ne peut tout simplement pas le faire.

En outre, avec JSON.NET, vous pouvez le personnaliser pour créer des modèles de vue Knockout complets, sans avoir à utiliser le plug-in de mappage.

J'ai écrit un exemple de bibliothèque appelée FluentJson.NET qui vous permet de faire des choses dans Razor comme:

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

Et obtenir:

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

Vous pouvez ainsi obtenir un modèle de vue Knockout sans passer par des cerceaux côté client.

Vous pouvez facilement étendre quelque chose comme ça pour gérer les valeurs de date comme vous le préférez.

13
Paul Tyng

Je suggérerais une approche intermédiaire via ko.mapping.fromJS( data, mapping ), ce qui vous permettrait de personnaliser même avec un objet défini par l'utilisateur. 

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 );

le paramètre mapping vous permet de gérer facilement les modifications et peut également être exploité facilement avec des tableaux. 

6
Andres Toro

La meilleure façon de gérer les dates dans knockoutjs est d'utiliser la bibliothèque de moments et de gérer les dates comme un boss. Vous pouvez facilement traiter des dates comme/Date (-62135578800000) /. Pas besoin de vous soucier de la façon dont vous sérialisez la date dans le contrôleur. 

Approche 1: vue directe:

Disons que votre modèle de masquage obtient cette date dans une observable appelée sentDate et qu’il a maintenant la valeur/Date (-62135578800000) /. Pour le lier en vue, vous pouvez faire: 

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

Approche 2: En liaison personnalisée  

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) {

    }
};

Utilisation identique à celle que vous avez dite: 

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

momentjs prend en charge de nombreux formats de date et d'heure et des fonctions utilitaires sur les dates.

5
Ajay Kelkar

Une alternative plus propre à la réponse de @ photo_tom consiste à décorer la propriété avec IsoDateTimeConverter via l'attribut JsonConverter, comme suit:

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

Nous venons juste de répondre à cette question car nous avons également commencé à utiliser knockout.js sur notre application MVC3. Comme nous avons déjà jQuery datepicker et que nous devons formater les dates différemment selon les paramètres régionaux (le portail a différentes langues et différents formats sont présentés). langue), alors peut-être que ce mashup d'exigences technologiques se pose ailleurs et sera utile:

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) {
    }
};

Et dans la vue alors par exemple:

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

J'utilise le code suivant pour générer des chaînes de date courtes. Je l'utilise pour mes chaînes de date et 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);
    }

Cela génère le JSON suivant

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

Cela résulte propre code JavaScript pour moi.

2
photo_tom

J'utilise toujours un convertisseur de données au lieu d'envoyer des données directement au serveur pour résoudre les problèmes d'encodage ou d'analyse syntaxique du client, sans recourir à des outils supplémentaires.

Dans le fichier de modèle de vue Knockout JS, j'ajoute le code suivant avant la configuration du modèle de vue, qui intercepte les modèles sélectionnés du modèle de vue et utilise moment.js pour prendre en charge les conversions de date:

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

    return value;
};

Ensuite, j'utilise la variable dataConverter au lieu de data dans la méthode de sauvegarde ajax du modèle d'affichage:

// 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
            }
        });
    }
0
Ashraf Abusada

J'ai adoré la réponse d'Andres Toro, sauf que dans mon cas, les champs de saisie attendent des chaînes formatées. J'utilise donc JQuery pour formater mes dates selon le format préféré fourni par mon assistant @Html.ConvertDateFormat() J'espère que cela aidera quelqu'un le jour.

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);
0
Ashi