AngularJs – ASP.NET MVC Datetime JSON Serialize und Anzeigen mit Hilfe einer Date Direktive – Part 2


Wie bereits im Part 1 erwähnt, hier ein weiterer Beitrag über AngularJs und die Verwendung einer Date Direktive.

Wenn man als Webentwickler bereits das das Wort “Datum” hört, weiss man hier wird es höchstwahrscheinlich Probleme geben. Leider hat sich hier auch mit ASP.NET MVC 4/5 nichts geändert, denn hier wird weiterhin der Microsoft Serializer für JSON verwendet und dies ist NICHT der gleiche wie bei der WebAPI (Newtonsoft.Json). Dies bedeutet, das Datumswerte standardmäßig nicht im ISO (WebAPI) Format, sondern als “/Date(12334534534)/” serialisiert werden und dieses Format kann AngularJs nicht standardmäßig “umwandeln”.

Ich habe mich für eine Lösung auf .NET Seite entschieden und habe im Controller das JsonResult überschrieben und serialisiere meine JSON Daten jetzt mit “Newtonsoft.Json”. Dadurch wird das Datum im Standard ISO Format übermittelt z.B. “2014-05-26T22:14:25.8104782+02:00”.

Erstellen einer neuen JsonResult Klasse, die für die Serialisierung “Newtonsoft.Json” verwendet.

/// <summary>
/// JsonResult überschreiben - Damit wir hier ein ISO Datumsformat zurückgeben können.
/// ACHTUNG es muss ebenfalls eine neue BasisKlasse für Controller angegeben werden "BaseController"
/// 
/// http://wingkaiwan.com/2012/12/28/replacing-mvc-javascriptserializer-with-json-net-jsonserializer/
/// </summary>
public class JsonNetResult : JsonResult
{
    public JsonNetResult()
    {
        Settings = new JsonSerializerSettings
                       {
                           ReferenceLoopHandling = ReferenceLoopHandling.Error,
                           DefaultValueHandling = DefaultValueHandling.Populate,
                           MaxDepth = 100,
                           ObjectCreationHandling =  ObjectCreationHandling.Auto
                           
                       };
    }

    public JsonSerializerSettings Settings { get; private set; }

    public override void ExecuteResult(ControllerContext context)
    {
        if (context == null)
            throw new ArgumentNullException("context");
        if (this.JsonRequestBehavior == JsonRequestBehavior.DenyGet && 
            string.Equals(context.HttpContext.Request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase))
            throw new InvalidOperationException("JSON GET is not allowed");

        HttpResponseBase response = context.HttpContext.Response;
        response.ContentType = string.IsNullOrEmpty(this.ContentType) ? "application/json" : this.ContentType;

        if (this.ContentEncoding != null)
            response.ContentEncoding = this.ContentEncoding;
        if (this.Data == null)
            return;

        var scriptSerializer = JsonSerializer.Create(this.Settings);

        using (var sw = new StringWriter())
        {
            scriptSerializer.Serialize(sw, this.Data);
            response.Write(sw.ToString());
        }
    }
}

Um im Controller weiter wie gewohnt auf “JsonResult” zugreifen zu können, kann man noch eine Abstrakte Basisklasse anlegen, in der man “JsonResult” überschreibt.

public abstract class BaseController : Controller
{
    protected override JsonResult Json(object data, string contentType,
        Encoding contentEncoding, JsonRequestBehavior behavior)
    {
        return new JsonNetResult
        {
            Data = data,
            ContentType = contentType,
            ContentEncoding = contentEncoding,
            JsonRequestBehavior = behavior
        };
    }
}

Jetzt erhalten wir in JavaScript ein ISO formatiertes Datum. Wenn wir dieses Datum mit Hilfe von Angular Bearbeiten wollen und im Eingabefeld nicht das komplette ISO Format stehen soll muss man eine Direktive für das Anzeigen eines Datums erstellen. Wir verwenden hierfür zusätzlich noch die JavaScript Bibliothek momentJs um das ISO Datum in der AngularJs Direktive entsprechend zu parsen und das passende Format auszugeben.

Die Direktive selbst wird einfach per Attribut auf unserem input Feld eingebunden und wandelt für die Anzeige das ISO Datum in das Format um, welches wir angegeben haben und in das Model selbst wird wieder ein ISO Formatiertes Datum zurückgeschrieben. In meinem Beispiel verliert man hier natürlich die Uhrzeit die wir anfangs übergeben haben.

angular.module("app.datetimeDirectives", [])
       .directive("datetimeInputTwoWayFilter", function ($log) {
           return {
               //Damit das ISO Format Datum was wir per JSON von MVC bekommen
               //auch im Standard Format angezeigt wird, kann man keinen Standard
               //Filter verwenden bei "ng-model". Sondern man muss über die Direktive gehen.
               //Die direktive wird als Attribut beim passenden Inputfeld gesetzt.
               //Wir verwenden momentJs um das Datum aus dem ISO Format zu parsen und dann
               //wieder entsprechend anzuzeigen beim "formater" und beim "parser" geben wir wieder
               //das passende ISO Format an unser Model zurück.
               require: "ngModel",
               link: function (scope, element, attrs, ngModelCtrl) {
                   //http://java.dzone.com/articles/parsers-and-formatters-custom
                   ngModelCtrl.$parsers.push(function (data) {
                       //convert data from view format to model format
                       var time = new moment(data, "DD.MM.YYYY");
                       //Wieder ins ISO Format zurückkonvertieren
                       return time.format(); //converted
                   });

                   ngModelCtrl.$formatters.push(function (data) {
                       //das Datum vom ISO in das unten angegebene Format überführen.
                       var time = new moment(data);
                       data = time.format("DD.MM.YYYY");
                       //convert data from model format to view format
                       return data; //converted
                   });
               }
           }
       })

Verwendet wird die Direktive dann folgendermaßen

<input type="text" ng-model="DataModel.Startdatum" datetime-input-two-way-filter class="form-control" name="inputStartDatum" placeholder="Startdatum">

Im Eingabefeld für “Startdatum” sehen wir unsere Direktive live in Action und im Eingabefeld “Enddatum” habe ich die Direktive entfernt und hier wird das ISO Formatierte Datum angezeigt.

image

Wenn man den Standard Serialisierer verwendet von Microsoft, dann sieht das ganze ohne Direktive z.B. folgendermaßen aus

image

MomentJs würde zwar auch diesen Datumstring wieder fehlerfrei in ein “richtiges” Datum parsen, ich würde hier persönlich aber das ISO Format vorziehen, da man hier auch direkt beim Debuggen ein konkretes Datum “auslesen” kann und nicht auf den String “/Date(1234…)/” angewiesen ist.

(Es ist also auch möglich ohne zusätzlichen Aufwand auf .NET Seite das Datum entsprechend anzuzeigen – dies habe ich auch erst beim Schreiben dieses Beitrags gemerkt – momentJs scheint hier ganze Arbeit zu leisten.)

Advertisements

2 Gedanken zu „AngularJs – ASP.NET MVC Datetime JSON Serialize und Anzeigen mit Hilfe einer Date Direktive – Part 2

  1. Pingback: AngularJS – DropDownListe (select/option) – Part 1 | SquadWuschel's Blog

  2. Pingback: ASP.NET MVC eigener JsonConverter für Decimal Datentyp mit CultureInfo | SquadWuschel's Blog

Kommentar verfassen

Trage deine Daten unten ein oder klicke ein Icon um dich einzuloggen:

WordPress.com-Logo

Du kommentierst mit Deinem WordPress.com-Konto. Abmelden / Ändern )

Twitter-Bild

Du kommentierst mit Deinem Twitter-Konto. Abmelden / Ändern )

Facebook-Foto

Du kommentierst mit Deinem Facebook-Konto. Abmelden / Ändern )

Google+ Foto

Du kommentierst mit Deinem Google+-Konto. Abmelden / Ändern )

Verbinde mit %s