Angular 2 PathLocationStrategy y Asp.Net Core Mvc

Si has hosteado una aplicación de angular 1 o angular 2 en Asp.Net Core Mvc una de las cosas con las que te habrás peleado es con la configuración del routing de cliente.

En angular 1, por defecto se utiliza el # como separador para identificar la parte de la url que define la ruta de cliente, pero en angular 2, ya no es así, y no se utiliza ningún separador (aunque se puede cambiar para que vuelva a utilizar el #).

Así, una ruta que en angular 1 podría ser http://myapp.com/#ruta/local, por defecto en angular 2 sería http://myapp.com/ruta/local.

Esto supone un problema cuando se realiza la petición al servidor, ya que como es una ruta normal (sin #), el framework intentará buscar el controlador y el método de acción pare devolver el Html generado, pero en realidad no va a existir y recibiremos un bonito 404.

Hay varias opciones para configurar el framework para que soporte routing de cliente, pero la más sencilla es usar el paquete de NuGet Microsoft.AspNetCore.SpaServices.

Entre otras cosas, nos ofrece un método de extensión sobre IRouteBuilder para registrar un controlador y método de acción que sirva de fallback para todas las rutas de cliente que no sepa encontrar.

app.UseMvc(routes =>
{
    routes.MapRoute(
        name: "default",
        template: "{controller=Home}/{action=Index}/{id?}");

    routes.MapSpaFallbackRoute("spa-fallback", new { controller = "Home", action = "Index" });
});

En esta aplicación, la página que contiene la SPA se sirve desde el controlador HomeController y la acción Index. Así que cualquier ruta que no se corresponda con un controlador y método de acción existente, entenderemos que forma parte de la SPA. Por lo tanto, devolveremos el punto de entrada de la SPA para que angular, en este caso, pueda gestionar sin problemas la parte de la url que corresponda al routing local.

Si estás trabajando con Asp.Net Core Mvc y angular (o React, o Knockout…), te recomiendo que te pases por este repo de GitHub https://github.com/aspnet/JavaScriptServices y revises algunas joyitas interesantes que aparecen por allí.

¡Que lo disfrutes!

Angular 1.x y las fechas

Trabajar con fechas en cualquier sistema siempre ha sido uno de los puntos más conflictivos y propensos a fallos. Entender la gestión que las diferentes plataformas hacen de las fechas locales, las fechas UTC, las zonas horarias, los formatos de serialización, … no es trivial. Panificar cómo debe almacenar las fechas nuestro sistema y cómo las debe representar para que sean leidas y modificadas por el usuario, exige un mínimo de panificación.

En las aplicaciones web en general, tenemos que tener en cuenta las características del objeto Date en javascript (por ejemplo que los meses empiezan en 0!), el comportamiento de la serialización y deserialización a JSON para que viaje por la red, y el comportamiento de la capa de serialización-deserialización de nuestra tecnología de backend.

Una de las caracteríasticas que tiene el objeto Date, es que va a representar siempre la fecha en la zona horaria configurada en el navegador (normalmente la del sistema), y que por lo tanto, para representar horas UTC vamos a tener que hacer algo de “magia” una de las librerías que facilitan enormemente manipular fechas en javascript es moment, pero no nos va a hacer falta siempre.

Angular viene con dos pequeñas joyas, poco conocidas, para poder configurar como vamos a bindear las fechas.

ng-model con fechas

Cuando bindeamos un input[date] con ng-model, la fecha que vamos a recibir en el modelo va a ser siempre la fecha local. Es decir, que si yo selecciono el día 12/03/2016 en el input y, en mi caso estoy en ese momento en la zona horaria GMT + 1, la fecha que obtendré es 12/03/2016 00:00:00 GMT+1. El problema es que esa fecha, serializada a JSON (por lo tanto UTC) se representará como 11/03/2016T23:00:00Z.

Evidentemente, si lo que estoy bindeando, por ejemplo, es un filtro de fecha que voy a enviar al servidor, no obtendré los resultados esperados, ya que mi intención es filtrar por la fecha 12/03/2016 y en realizad estoy enviando el 11/03/2016.

Afortunadamente, desde la versión 1.3 de angular, en la directiva ng-model-options hay una configuración para configurar la zona horaria.

<input type="date" ng-model-options="{timezone: 'UTC'}" ng-model="$dates.dateTimezone">

Con esta configuración, con el ejemplo anterior, cuando yo seleccione el 12/03/2016  en el input, obtendré el 12/03/2016 01:00:00 GMT+1, que cuando lo serializamos a JSON, se representa como 12/03/2016T00:00:00Z.

Por lo tanto, ya enviaremos la fecha correcta del filtro al servidor, y sin utilizar ninguna librería externa.

filter date

Esta misma configuración la tenemos disponibles en el filtro date. Este filtro permite recibir como segundo parámetro la zona horaria en la que queremos que se visualice la fecha.

<div>{{ $dates.asDate | date:'yyyy-MM-dd HH:mm:ss.sss':'UTC' }}</div>

Con lo cual, nos resultará muy sencillo mostrar al usuario fechas en formato UTC, otra vez sin necesidad de usar librerías externas.

Además otra cuestión muy interesante con el filtro date es que soporta objetos Date, strings y number. Por lo tanto, si recibimos la fecha serializada en JSON en el modelo, y sólo queremos mostrarla (no editarla), podemos bindearla tal cual sin necesidad de convertirla a un objeto Date. Si necesitamos editarla, sí que tendremos que hacer esa conversión, porque ng-model sobre un input[date] sí que necesita bindear sobre una propiedad de tipo Date.

Os dejo un plunk con algunas pruebas sobre estos temas para jugar.

Hasta la próxima!