Programación

Migrar AntD 4 a 5 con estilos less y adaptaciones de tema

Recientemente he estado trabajando junto con mi equipo en unos proyectos en react que utilizan la librería de AntD. El proyecto originalmente hace 5 años a la fecha de escribir este artículo. Durante estos años he ido migrando versiones de React y AntD hasta la 4.

Cuando los chicos de AntD publicaron la versión 5 le eché un vistazo y decidí esperar un poco a que estuviera algo más madura y cuando llegó el momento de migrar, llegaron los problemas, pues AntD elimina los estilos less.

Nosotros nos apoyamos en craco para hacer una adaptación de loaders de less en el proyecto de React, pero claro, esto ya no era posible en la versión AntD5. A continuación quiero detallar el proceso que seguimos en su momento para migrar la versión de AntD4 a AntD5 y no perder los estilos customizados por el camino.

Existe una guía de migración de AntD 4 a AntD 5, donde detallan los cambios entre la versión 4 y 5 de la librería y los pasos a seguir.

Lo primero, bastante obvio, es actualizar el paquete de AntD a su versión 5.

Una vez migrado, vamos a ver que hacemos con less. En nuestro caso, como ya utilizamos un loader de craco de less para babel, no tenemos que hacer nada especial que no aparezca en la guía. Tenemos que añadir el paquete @ant-design/compatible que lo haremos con el comando

yarn add @ant-design/compatible

Desde la versión 5 de AntD, ya no se utiliza less sino unos estilos que se denominan css-in-js, por lo a través de una paquete de compatibilidad tenemos que habilitar los estilos de less. Esto no es 100% necesario, depende de cada proyecto. En nuestro archivo de configuración de craco tenemos que crear lo que han llamado theme y tokens. Viene muy bien explicado en la documentación de forma gráfica donde tenemos los seed que son las semillas del template (si queremos temas claros, oscuros, etc) y de ahí descienden los mapas y tokens que generarán el estilo visual final. Para que todo funcione incluiremos el siguiente código en nuestro inicio, da igual si es craco o cualquier otro sistema de adaptación.

const { convertLegacyToken } = require('@ant-design/compatible/lib');
const { theme } = require('antd/lib');

const { defaultAlgorithm, defaultSeed } = theme;

const seed = {
	...defaultSeed,
	colorPrimary: '#EB4034', //Adaptación de less del color primario
	colorBgContainer: '#FAFEFE' //Adaptación de less del fondo del contenedor
};

const mapToken = defaultAlgorithm(seed);
const v4Token = convertLegacyToken(mapToken);

...

loader: 'less-loader',
options: {
	lessLoaderOptions: {
		lessOptions: {
			modifyVars: v4Token /* {
				'@primary-color': '#EB4034',
				'@layout-body-background': '#FAFEFE',
				'@select-item-selected': 'white'
			} */,
			javascriptEnabled: true
		}
	}
}

En nuestro caso la adaptación era muy sencilla, en lugar de enviar a less las variables sobrescritas, hay que pasar el token generado con esas mismas variables sobrescritas.

El siguiente paso se trata de modificar los archivos less. En nuestro caso teníamos como primera linea del archivo App.less en el raíz de la carpeta src la siguiente línea que es necesaria comentar:

//@import '~antd/dist/antd.less';

Si no quitamos esta linea estamos forzando a traer el less si está en el node_modules, pues en nuestro caso es un monorepo donde hemos empezado a migrar primero un proyecto pequeño y puede existir (en la versión 5 de AntD no debería existir y daría error).

Bien, ya lo tenemos listo pero el theme nos aparece en azul. Hasta aquí fue todo sencillo ya que el siguiente paso no es como tal de la migración sino de leer la nueva documentación de AntD5. La sobrescritura de variables en esta nueva versión se hace a través de un ConfigProvider. En nuestro proyecto ya utilizabamos dicho ConfigProvider para forzar el locale a español.

En nuestro proyecto, el ConfigProvider engloba al Router que es el React-Router de la aplicación de la siguiente manera:

<ConfigProvider
	locale={es_ES}
	theme={{
		algorithm: theme.defaultAlgorithm,
		token: { colorPrimary: '#EB4034', colorBgLayout: '#FAFEFE' }
	}
}>
	</Routes>
</ConfigProvider>

Espero que os haya servido de ayuda.

Hacer una consulta MongoDB comprobando que un atributo array no esté vacío

En el ejemplo de obtener resultados aleatorios en una consulta de MongoDB y mongoose utilizaba un where un tanto especial. Este where es para comprobar que un atributo de tipo array que está contenido en el modelo tiene datos.

Primero definamos un ejemplo de modelo simple con el que vamos a trabajar. Lo detallo en formato JSON para no anclarnos a un entorno:

{
  name: "Nombre",
  code: "Código",
  screenshots: [],
  description: "Descripción"
}

Ahora con el modelo definido veamos como utilizarlo. Para ello debemos fijarnos en la sentencia where que vamos a utilizar.

[code lang=»json»]{ screenshots: {$exists: true, $not: {$size: 0}} }[/code]

En este where, sobre screenshots realizamos 2 comprobaciones. La primera comprobación es utilizar «$exists: true» donde indicamos que la propiedad necesariamente debe existir. Recordemos en las bases de datos de documentos como MongoDB no necesariamente deben coincidir todos los modelos en una colección (tabla) de la base de datos.

Lo segundo que comprobamos es «$not» negamos que el tamaño del array sea 0 o lo que es lo mismo «$size: 0», de esta forma, le pedimos a MongoDB que nos de solo modelos que tengan la propiedad screenshots y que el tamaño de screenshots sea mayor de 0.

Ahora volvamos a ver el ejemplo que usamos con mongoose.

var where = { screenshots: {$exists: true, $not: {$size: 0}} };

mongooseGameModel.count().where(where).exec(function(err, gameCount) {
  var rand = Math.floor(Math.random() * gameCount);

  mongooseGameModel.findOne().where(where).skip(rand).populate('developer').exec(function(err, game) {
    res.locals.randomGame = game;
    next();
  });
});

En este ejemplo utilizando mongoose contra MongoDB obtenemos un juego aleatorio de un listado que cumpla la condición de que screenshots no esté vacío.

Obtener registros aleatorios con MongoDB

En una base de datos NoSQL como es MongoDB nos asaltan dudas constantemente. En este caso, ¿Cómo obtener un único registro aleatorio? A continuación os detallo como obtener este dato.

Para obtener un registro aleatorio debemos de realizar una consulta de un único elemento pero saltando los x primeros registros. De esta forma obtendremos el resultado. Nada mas fácil que verlo con un ejemplo. Para ello pondré 2 ejemplos uno directamente haciendo la consulta sobre MongoDB y otro utilizando ODM de Mongoose.

En MongoDB se haría de la siguiente forma.

[code lang=»javascript»]db.games.find().limit(-1).skip( _rand() * db.games.count() )[/code]

Si tenemos una tabla games con un listado de juegos y queremos seleccionar un juego aleatorio, lo podríamos hacer así. Seleccionamos todos los juegos, establecemos el limit a -1 y saltamos un aleatorio.

Otro método sería utilizando mongoose, este ya solo como programación.

mongooseModel.count().exec(function(err, resultCount) {
  var rand = Math.floor(Math.random() *resultCount);
  mongooseModel.findOne().skip(rand).exec(function(err, result) {
    console.log(result);
  });
});

Como podemos ver, es un lo mismo que hacerlo directamente sobre MongoDB pero añadiendo algo de complejidad en el proceso por las llamadas asíncronas. En primer lugar deberemos obtener el número de resultados, una vez los tengamos, podemos calcular el aleatorio y obtener con findOne el resultado que queremos.

Ya para terminar un buen ejemplo de como utilizar este código, es utilizando where para filtrar los resultados, que deberemos aplicar tanto a la hora de obtener el contador como cuando buscamos el resultado, ya que si no lo aplicamos en conjunto, puede fallarnos todo el código.

var where = { screenshots: {$exists: true, $not: {$size: 0}} };

mongooseGameModel.count().where(where).exec(function(err, gameCount) {
  var rand = Math.floor(Math.random() * gameCount);

  mongooseGameModel.findOne().where(where).skip(rand).populate('developer').exec(function(err, game) {
    res.locals.randomGame = game;
    next();
  });
});

¿Qué es un protocolo de ObjetiveC y para que se usan los protocolos en ObjetiveC?

Cuando programamos en ObjetiveC para iphone o ipad, utilizando funciones mas avanzadas de la interfaz gráfica nos encontramos con delegados, llamados también delegates. En estos casos, por ejemplo cuando tenemos un UIScrollView que captura el evento – (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView que utilizamos para controlar cuando ese UIScrollView se ha parado. Para controlar esto lo hacemos mediante el uso de los delegados. Pero mejor verlo con un ejemplo que se vea mas claro.

@implementation IndalcasaViewController

– (void)viewDidLoad {
[super viewDidLoad];
self.indalScroll.delegate = self;
}

– (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
int page = scrollView.contentOffset.x / scrollView.frame.size.width;

NSLog(@»Hemos cambiado a la pagina %i», page);
}

@end

Tras este ejemplo vemos como funciona un delegado en una clase normal de ObjetiveC. Pero aún no entendemos lo que significa ser un delegado. Un delegado no es mas que un nombre de convención, que podemos llamar como queramos y que utilizaremos como disparador, donde el objeto que está almacenado implementa una serie de funcionalidades definidas en un protocolo.

Se que ahora ha quedado claro del todo :P, pero mejor verlo en un ejemplo.

@interface IndalcasaViewController : UIViewController
@end

Como vemos en el ejemplo anterior, a la hora de extender la clase UIViewController decimos que implementa el protocolo UIScrollViewDelegate. Para los mas veteranos desarrolladores, un protocolo no es mas que una interfaz. Para los mas novatos, una interfaz es como una clase donde se definen llamadas pero no se implementa código.

Para verlo todo mas sencillo, vamos a poner un último ejemplo. Lo que vamos a hacer es definir otro UIViewController al que instanciaremos en el viewDidLoad y que implementará un metodo vacío llamado «cambioPagina» que recibirá la página por parámetro.

IndalcasaViewController.h
@interface IndalcasaViewController : UIViewController

@property UIViewController delegate;
@property (weak, nonatomic) IBOutlet UIScrollView *indalScroll;

@end

IndalcasaViewController.m
@implementation IndalcasaViewController

– (void)viewDidLoad {
[super viewDidLoad];
self.indalScroll.delegate = self;
}

– (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
int page = scrollView.contentOffset.x / scrollView.frame.size.width;

NSLog(@»Hemos cambiado a la pagina %i», page);

[self.delegate cambioPagina:page];
}

@end

DelegadoViewController.h
@interface DelegadoViewController : UIViewController
@end

@protocol IndalcasaDelegate

-(void)cambioPagina:(int)pagina;

@end

DelegadoViewController.m
@implementation DelegadoViewController

– (void)cambioPagina:(int)pagina {
NSLog(@»Hemos cambiado a la pagina %i con un delegado», pagina);
}

@end

Como vemos en el ejemplo, lo que hacemos es que cuando capturamos el evento scrollViewDidEndDecelerating, que nos indica que se ha parado el scroll, llamamos a un delegado, previamente seteado que implementa el protocolo (intefaz) IndalcasaDelegate. Como sabemos que el delegado, al implementar el protocolo IndalcasaDelegate tiene un método cambioPagina que recibe un entero con la página, podemos llamar al método desde el delegado.

Comentar también que en muchos casos, nos encontraremos delegados de tipo id y no de un tipo concreto. Los tipos id vendrían a ser tipos de objetos genéricos, algo parecido al object de cualquier lenguaje.

Por que usar un framework para frontend web como bootstrap

CSS3Desde hace unos años han ido apareciendo de forma generalizada y bien desarrollada lo que todo el mundo solía hacer en sus desarrollos web y son pequeños frameworks para el frontend. Quien haya maquetado de forma continuada, con el tiempo habrá ido reutilizando técnicas entre proyectos. Por ejemplo, una de ellas es el uso de estilos de reseteo como el ya clásico reset.css o la creación y reutilización de componentes básicos como botones, migas de pan, etc.

Hace unos 3 o 4 años empezó a surgir una tendencia de creación de sistemas de grid, organizando nuestra web en columnas. El uso del grid nos facilita el trabajo, siempre y cuando no vayamos buscando el pixelperfect, ya que nos permite adaptar el ancho de las columnas, dar estilos generales y modificar la apariencia de nuestra web con pocas lineas de css sobre el grid ya creado.

Hace un par de años que empezaron a nacer los frameworks completos para frontend. Estos frameworks no son solo una base de estilos, sino también componentes de uso general que usamos a diario. A veces y según el desconocimiento de la gente, piensa como frameworks frontend cosas que no lo son. Para aclararlo creo que lo mas sencillo es quedarnos en jQuery. jQuery tiene varias ramas, la librería, que no es mas que una librería de manipulación de DOM, jQueryUI que es un framework de componentes frontend y jQuery Mobile que viene a ser no un framework frontend sino ya un fullstack de frontend. La diferencia entre ellos es evidente, ya que jQueryUI no nos da una base de estilos, ni un sistema del que poder extender nuestra página, sino unos componentes como botones, tabs, sliders, etc. con los que podemos crear componentes mucho mas ricos que los nativos de HTML. Por otro lado tenemos el fullstack de jQuery Mobile que no solo nos da un theme con estilos básicos sobre los que desarrollar, sino todo el sistema de navegación, control de menús, formatos, datos, etc.

¿Qué es un framework frontend para web? No es mas que un conjunto de librerías y estilos básicos que nos permiten construir interfaces frontend de forma rápida y personalizable. Comparandolo con jQueryUI, sería como añadir unos estilos de scaffolding para la construcción de la estructura de la página y un grid de maquetación. Si por otro lado lo comparamos con jQuery Mobile, sería lo mismo pero eliminando la parte en la que jQuery Mobile nos construye todo el sistema de navegación entre páginas (es un ejemplo), bajandolo por así decir de fullstack a framework.

bootstrap-logo

Los sistemas de scaffolding nos ayudan a crear prototipos de webs de forma rápida. Podemos generar de forma rápida, paginas, menús, secciones, formularios, todo con estilos genéricos. Una vez desarrollado nuestro scaffolding básico, podemos dedicar esfuerzos a personalizar el estilo de la página. Hay que tener en cuenta que donde se diferencia de la forma clásica que teniamos de trabajar si éramos ordenados es que no nos tenemos que preocupar de la evolución de nuestro framework de base de estilos y que ademas podemos reaprovechar los plugins que desarrolle la comunidad.

Ahora un breve listado con los 4 frameworks mas útiles, aunque hay muchos mas:

Espero que os pueda servir como punto de entrada para investigar y aplicar posteriormente en vuestros proyectos.

Animaciones en CSS3: transiciones en CSS3

Hace ya tiempo que no escribo y eso no puede ser. Así que hoy os quiero hablar de animaciones, en concreto me voy a centrar en transiciones con CSS3 o como se diría en ingles CSS3 transitions.

Lo primero es sentar unas bases de lo que es una transición. Básicamente cuando hablamos de transitions en CSS3, lo que hacemos referencia es una pequeña animación de css que se ejecutará como mezcla de 2 o mas clases. En concreto una transición de CSS3, lo que indica es que propiedades cambiarán con una animación. Pero es mas fácil con un ejemplo:

.clase-indalcasa {
  -webkit-transition: all 1s ease-in-out 0.2s;
  -moz-transition: all 1s ease-in-out 0.2s;
  -ms-transition: all 1s ease-in-out 0.2s;
  -o-transition: all 1s ease-in-out 0.2s;
  transition: all 1s ease-in-out 0.2s;
}

Si nos damos cuenta del código anterior, que se repite una y otra vez para dar salida a todos los metas de navegador experimental, veremos que la sintaxis básica es:

transition: [propiedades] [duración] [functión de tiempo] [tiempo de espera, retardo]

Como podemos ver, ahora entendemos mejor que hace referencia en la clase, tomando con concepto que todos los elementos son opcionales. A continuación una explicación más detallada:

  • propiedades (properties): son las propiedades que van a ser afectadas por la transición. Es un listado separado por comas «,». Por ejemplo, si queremos aplicar a todas las propiedades de un elemento usariamos «all», mientras que si queremos afectar por ejemplo a la altura y anchura usariamos «width, height» y así ir añadiendo propiedades a las que afectar. Al final dejare una tabla con una relación de los propiedades a afectar.
  • duración (duration): la duración se indica o bien en segundos o en milisegundos. Para ello después del número de cantidad, indicamos 0.5s o 500ms para indicar medio segundo. Si tenemos varias propiedades separadas por comas, podemos indicar los tiempos de manera similar para cada una de las propiedades.
  • función de tiempo (timing function): está es quizas la configuración mas interesante, ya que nos permite indicar que función de tiempo vamos a utilizar en la transición. Por defecto hay definidas varias definidas o utilizar un cubic-bezier para definir nosotros una personalizada.
    • ease: mantiene una aceleración y frenada al principio y al final de la animación.
    • ease-in: tiene una aceleración y el final es constante hasta que termina.
    • ease-out: no tiene aceleración, sino que alcanza su tope desde el principio pero va frenando poco a poco.
    • ease-in-out: similar a ease pero con la aceleración y frenada mas prolongadas.
    • linear: es como su nombre indica, linear y constante en el tiempo
    • cubic-bezier: nos permite definir una función de curva de bezier personalizada. Esto da para todo un tema aparte y para empezar a entender que es: Curva de Bezier en Wikipedia
  • tiempo de espera, retardo (delay): al igual que la duración, se indica en segundos o milisegundos.

Ahora algunos ejemplos:

pasame
por encima

El código de este ejemplo sería:

.indalcasa-transition-hover {
  width: 100px;
  height: 100px;
  background: rgba(255, 0, 0, 0.3);
  -webkit-transition: all 2s ease-in-out;
  margin: 20px auto;
  line-height: 50px;
  text-align: center;			
}

.indalcasa-transition-hover:hover {
  width: 500px;
  height: 500px;
  background: rgba(0, 255, 0, 0.3);
  font-size: 50px;
  line-height: 250px;
  border-radius: 250px
}
<div class="indalcasa-transition-hover">pasame<br/>por encima</div>

Por último y para terminar esta breve explicación, una tabla de propiedades que se pueden o no cambiar con las transiciones.

Nombre de la propiedad Tipo de valor
background-color color: nombre, hexadecimal, rgb, rgba
background-image gradientes de css
background-position porcentaje, tamaño: pixeles, em, etc
border-bottom-color color: nombre, hexadecimal, rgb, rgba
border-bottom-width tamaño: pixeles, em, etc
border-color color: nombre, hexadecimal, rgb, rgba
border-left-color color: nombre, hexadecimal, rgb, rgba
border-left-width tamaño: pixeles, em, etc
border-right-color color: nombre, hexadecimal, rgb, rgba
border-right-width tamaño: pixeles, em, etc
border-spacing tamaño: pixeles, em, etc
border-top-color color: nombre, hexadecimal, rgb, rgba
border-top-width tamaño: pixeles, em, etc
border-width tamaño: pixeles, em, etc
bottom tamaño: pixeles, em, etc, porcentaje
color color: nombre, hexadecimal, rgb, rgba
crop rectangulo
font-size tamaño: pixeles, em, etc, porcentaje
font-weight número, nombre
grid-* varios
height tamaño: pixeles, em, etc, porcentaje
left tamaño: pixeles, em, etc, porcentaje
letter-spacing tamaño: pixeles, em, etc
line-height número, tamaño: pixeles, em, etc, porcentaje
margin-bottom tamaño: pixeles, em, etc
margin-left tamaño: pixeles, em, etc
margin-right tamaño: pixeles, em, etc
margin-top tamaño: pixeles, em, etc
max-height tamaño: pixeles, em, etc, porcentaje
max-width tamaño: pixeles, em, etc, porcentaje
min-height tamaño: pixeles, em, etc, porcentaje
min-width tamaño: pixeles, em, etc, porcentaje
opacity número de opacidad, de 0.0 a X
outline-color color: nombre, hexadecimal, rgb, rgba
outline-offset número
outline-width tamaño: pixeles, em, etc
padding-bottom tamaño: pixeles, em, etc
padding-left tamaño: pixeles, em, etc
padding-right tamaño: pixeles, em, etc
padding-top tamaño: pixeles, em, etc
right tamaño: pixeles, em, etc, porcentaje
text-indent tamaño: pixeles, em, etc, porcentaje
text-shadow valores
top tamaño: pixeles, em, etc, porcentaje
vertical-align nombres, tamaño: pixeles, em, etc, porcentaje
visibility valores de css
width tamaño: pixeles, em, etc, porcentaje
word-spacing tamaño: pixeles, em, etc, porcentaje
z-index posición de entero
zoom número

Tabla de codificaciones de caracteres entre ANSI, UTF-8, Javascript, HTML

ene-virguilillaCuando hacemos una página web en utf8, al escribir una cadena de texto en javascript que contenga acentos, tildes, eñes, signos de interrogación y demás caracteres considerados especiales, se pintara de una forma extraña en el html. Esto es porque javascript tiene su propio sistema de codificación, al igual que el html. A continuación os dejo una tabla con los principales caracteres que se utilizan, ya que es útil tenerlos a mano para poder escribirlos:

ANSI UTF-8 JAVASCRIPT HTML
Á Á u00c1 &#193;
á á u00e1 &#225;
É Ã‰ u00c9 &#201;
é é u00e9 &#233;
Í Ã u00cd &#205;
í í u00ed &#237;
Ó Ã“ u00d3 &#211;
ó ó u00f3 &#243;
Ú Ãš u00da &#218;
ú ú u00fa &#250;
Ñ Ã‘ u00d1 &#209;
ñ ñ u00f1 &#241;
¿ ¿ u00bf &#191;

Y después de esta tabla a modo de apunte, os dejo la tabla completa de codificación de caracteres como eñes, acentos, tildes y demás caracteres entre ANSI, UTF8, Javascript y HTML:

ANSI UTF-8 JAVASCRIPT HTML
 u00a0 &#160;
¡ ¡ u00a1 &#161;
¢ ¢ u00a2 &#162;
£ £ u00a3 &#163;
¤ ¤ u00a4 &#164;
¥ Â¥ u00a5 &#165;
¦ ¦ u00a6 &#166;
§ § u00a7 &#167;
¨ ¨ u00a8 &#168;
© © u00a9 &#169;
ª ª u00aa &#170;
« « u00ab &#171;
¬ ¬ u00ac &#172;
­ ­ u00ad &#173;
® ® u00ae &#174;
¯ ¯ u00af &#175;
° ° u00b0 &#176;
± ± u00b1 &#177;
² ² u00b2 &#178;
³ ³ u00b3 &#179;
´ ´ u00b4 &#180;
µ µ u00b5 &#181;
¶ u00b6 &#182;
· · u00b7 &#183;
¸ ¸ u00b8 &#184;
¹ ¹ u00b9 &#185;
º º u00ba &#186;
» » u00bb &#187;
¼ ¼ u00bc &#188;
½ ½ u00bd &#189;
¾ ¾ u00be &#190;
¿ ¿ u00bf &#191;
À À u00c0 &#192;
Á Á u00c1 &#193;
 Â u00c2 &#194;
à Ã u00c3 &#195;
Ä Ã„ u00c4 &#196;
Å Ã… u00c5 &#197;
Æ Ã† u00c6 &#198;
Ç Ã‡ u00c7 &#199;
È Ãˆ u00c8 &#200;
É Ã‰ u00c9 &#201;
Ê ÃŠ u00ca &#202;
Ë Ã‹ u00cb &#203;
Ì ÃŒ u00cc &#204;
Í Ã u00cd &#205;
Î ÃŽ u00ce &#206;
Ï Ã u00cf &#207;
РÐ u00d0 &#208;
Ñ Ã‘ u00d1 &#209;
Ò Ã’ u00d2 &#210;
Ó Ã“ u00d3 &#211;
Ô Ã” u00d4 &#212;
Õ Ã• u00d5 &#213;
Ö Ã– u00d6 &#214;
× Ã— u00d7 &#215;
Ø Ã˜ u00d8 &#216;
٠Ù u00d9 &#217;
Ú Ãš u00da &#218;
Û Ã› u00db &#219;
Ü Ãœ u00dc &#220;
Ý Ã u00dd &#221;
Þ Ãž u00de &#222;
ß ÃŸ u00df &#223;
à Ã u00e0 &#224;
á á u00e1 &#225;
â â u00e2 &#226;
ã ã u00e3 &#227;
ä ä u00e4 &#228;
å Ã¥ u00e5 &#229;
æ æ u00e6 &#230;
ç ç u00e7 &#231;
è è u00e8 &#232;
é é u00e9 &#233;
ê ê u00ea &#234;
ë ë u00eb &#235;
ì ì u00ec &#236;
í í u00ed &#237;
î î u00ee &#238;
ï ï u00ef &#239;
ð ð u00f0 &#240;
ñ ñ u00f1 &#241;
ò ò u00f2 &#242;
ó ó u00f3 &#243;
ô ô u00f4 &#244;
õ õ u00f5 &#245;
ö ö u00f6 &#246;
÷ ÷ u00f7 &#247;
ø ø u00f8 &#248;
ù ù u00f9 &#249;
ú ú u00fa &#250;
û û u00fb &#251;
ü ü u00fc &#252;
ý ý u00fd &#253;
þ þ u00fe &#254;
ÿ ÿ u00ff &#255;

Espero que os sirva de ayuda tanto como a mi.

ASP.Net MVC Razor, la evolución desde apsx webforms hacia MVC y fundamentos básicos de ASP.Net MVC

aspnetQuienes hayan trabajado alguna vez con .Net, conocerán el sistema de plantillas de ASP.Net llamado webforms. Los webforms fueron un sistema de vistas con extensión aspx que podían ir o no acompañadas de un codebehind y controles. El objetivo de Microsoft cuando diseño esta parte del framework de .Net fue mover a los desarrolladores a crear aplicaciones en nube, que aunque ahora este tan de moda el termino nube, estamos en un contexto del año 2000. Los webforms permitían desarrollar una web, tal y como los desarrolladores del viejo Visual Basic hacían sus aplicaciones de escritorio, donde las páginas eran llamadas formularios (de ahí el término webform) y los elementos tales como inputs, botones, etc, se arrastraban con un editor wysiwyg. Los antiguos webforms, aunque simulaban un entorno de escritorio, en cuanto a formularios, no eran otra cosa que webs y como tales podías tocar el html desde el editor y no solo tener la opción de editor wysiwyg, encontrando bajo el capo una web de código espagueti tal y como siempre se habló de php por ejemplo, salvo que parte de la lógica estaba en el codebehind. Esta última parte era lo peor de todo, puesto que si el desarrollador era experto, el código espagueti era minúsculo, pero a medida que descendía la pericia del programador, aumentaba el tamaño tanto del codebehind como del propio espagueti html que era ese aspx webform.

Microsoft publico todo esto en las primeras versiones del .Net framework y fue un bombazo, cualquiera podía hacer una web con muchos formularios en un tris, pero claro, ahí esta está el cliente para recordarnos que era muy feo y que lo querían mas bonito como habían visto por internet otras páginas. Es entonces cuando empiezan a verse los primeros problemas a la hora de realizar webs y es que aspnet 1 no tenia soporte para páginas maestras, por lo que los desarrolladores acababan en practicas comunes en otros lenguajes y que eran tan criticadas como copiar y pegar la misma estructura de página (controles, cabecera, menu, pie, etc) por todas y cada una de las páginas que tuviéramos en nuestro proyecto. Así que como novedad, Microsoft pensó que aparte de cambiar la forma de incluir los controles en una web, automatizando todo el proceso, penso que era hora de crear un concepto mas que necesario, una masterpage. Una masterpage, no es mas que como su nombre indica, una página maestra, digamos que todo ese contexto de cabeceras, menu, pie, etc que ibamos copiando de página a página pero que ahora se centraliza en un sitio, marcando en esa página maestra, donde queremos que se renderice el contenido de la página. Esto supuso un gran avance, ya que las páginas desarrolladas con ASP.Net, fueron mejorando en el punto que eran mas limpias. Aun y con todo, seguian los problemas, el auge de librerías de javascript dinámicas, con efectos y llamadas de ajax, dejaba en ridículo a controles muy cerrados tipo updatepanels y los desarrolladores pedían a gritos mas flexibilidad.

Cuando uno ha vivido toda la vida amamantado por la teta de Microsoft, no se percata de cierta rigidez a la hora de desarrollar una web con ASP.Net, pero quien tocara cualquier otro entorno, veia como en php y python, aparecian framework MVC, que permitian utilizar todas esas librerias de ajax pero sin montar un pipote (caos) de código para que funcionen bien. Es entonces cuando aparece MVC. El concepto de MVC es un concepto creado en 1979, no trae ninguna novedad como tal, pero si que introduce ese concepto en ASP.Net teniendo una serie de caracteristicas fundamentales (en un sistema normal sin mucha floritura):

  • Los controladores son siempre los que reciben las peticiones, ya no hay controladores genéricos ashx, servicios asmx, paginas aspx, controles ascx, ni masterpages master. Los controles son clases donde por defecto, sus métodos serán aciones del controlador.
  • Las vistas son solo eso, vistas, con sintaxis webform en MVC 1 y 2 y con Razor en MVC 3 y 4.
  • Los modelos son clases con propiedades multifunción, validar automáticamente datos, enviar y recibir formularios, sincronizar datos con la base de datos (Entity Framework Code First).
  • El proyecto es un binario cerrado, como ocurría en la primera versión de ASP.Net y en el resto si lo cambiabas a proposito, ya no hay mas código abierto con el que dejar a IIS que compile el código, como si fuera php.

Hasta aquí, una breve explicación de como surge MVC, pero ¿Y Razor? Razor es el nuevo sistema de templates de ASP.Net MVC. Quien haya programado con webforms, se habrá dado cuenta que en una página con un poco de lógica, acaba creciendo mucho y complicando la sintaxis con abre código <%, mete código, cierra código %>, haciendo que no solo crezca una página, sino que además, el código se vuelve mas y mas feo por momentos. Razor soluciona esto con 2 procedimientos, el primero es su cierre automático inteligente, es decir, la etiqueta de apertura será una arroba @, pero no tendrá cierre como tal. La segunda peculiaridad es que el código es mucho mas fluido puesto que se aprovecha del propio html para formar ciertas partes de la sintaxis. Ademas, Razor tiene algunas ventajas como son poder indicar un modelo que utiliza la página para hacer validaciones, formularios y pintar datos de una forma mas sencilla.

Veamos un par de ejemplos de código hecho con Razor frente al hecho con webform:

@model Indacalsa.Models.LoginModel

@using (Html.BeginForm("Login", "Account", FormMethod.Post)) {
  @Html.AntiForgeryToken()
  <fieldset id="login">
    <legend>Datos de acceso @ViewBag.Title</legend>
    <p>
      <label>
        <span class="title">Usuario:</span>
        @Html.TextBoxFor(m => m.UserName)
        @Html.ValidationMessageFor(m => m.UserName)
      </label>
    </p>
    <p class="center">
        <input type="submit" value="Acceder" class="btn" />
    </p>
  </fieldset>
}
<form method="post" action="/Login/Account.aspx" runat="server">
  <fieldset id="login">
    <legend>Datos de acceso <%=this.Title%></legend>
    <p>
      <label>
        <span class="title">Usuario:</span>
        <asp:TextBox name="UserName" id="UserName" runat="server" />
        <asp:RegularExpressionValidator ID="validatorUserName" runat="server" ControlToValidate="UserName" ErrorMessage="La longitud mínima es de 5 caracteres" ValidationExpression=".{5}.*" />
      </label>
    </p>
    <p class="center">
        <asp:Button value="Acceder" class="btn" runat="server" />
    </p>
  </fieldset>
</form>

Lo he simplificado, primero en Razor y luego en ASP.Net webforms. La parte de webforms la he simplificado sin utilizar el tipico form que ocupa toda la página y sin mucha historia, para dar un acercamiento de como va el tema. Aparte de este ejemplo hay que sacar la mala experiencia de utilizar controles de webform y sus ids mágicos, si es que luego queremos dinamizar la página con ajax o javascript.

Y para los que vengan de atras, aquí van algunas similitudes entre webform y Razor:

  • Las masterpage de webform ahora son llamadas Layouts
  • Los controles de webform ahora son vistas parciales, pero con la salvedad de que no tendrán codebehind.
  • Para especificar código espagueti o en linea en webforms se hace con <% codigo %> y <%=codigo%>. Con Razor se hace con @micodigo o @(micodigo_muy_complejo).
  • Para ir renderizando texto dentro del código de Razor ya no es necesario abrir y cerrar las etiquetas de codigo <% y %>, sino que podemos usar <text>texto a renderizar</text>
  • Los asp:content de las masterpages ahora son sections.

Espero que os guste y os sirva un poco a aquellos rehacios a migrar a MVC o desde otros lenguajes.

ASP.Net y los errores de «La operación no es válida debido al estado actual del objeto» y como solucionarlo con MaxHttpCollectionKeys

A veces en ASP.Net, se da el caso de que una página tiene un formulario inmenso, cuando por ejemplo queremos mandar un modelo gigantesco. Aunque de darse este caso, deberiamos plantearnos si no debemos cambiar nuestro código, puede que si lo ejecutamos nos de un error indicando «La operación no es válida debido al estado actual del objeto» o en ingles «Operation is not valid due to the current state of the object». Esto pasa porque hemos sobrepasado el límite máximo de parámetros que se pueden enviar.

La solución es bien sencilla, solo debemos modificar nuestro web.config para que admita mas parámetros

<configuration>
  <appSettings>
    <add key="aspnet:MaxHttpCollectionKeys" value="5000" />
  </appSettings>
</configuration>

Añadiendo la key MaxHttpCollectionKeys y aumentandola hasta 5000, el problema se soluciona.

Problema de codificación UTF8, Internet Explorer 8 y PHP

El otro día tuve que tocar una web antigua de un compañero que estaba hecha en PHP4. Al abrir los archivos para hacer unos cambios que me pidieron, estos estaban codificados en vete tu a saber que codificación, así que al hacer cambios se estaba autocodificando todo en UTF8. Al hacer pruebas, todo va bien, en Chrome, Safari y Firefox se ve todo estupendamente, incluso en Internet Explorer 9 se veia bien, eso si, no sin antes añadir en la página el meta indicando que está codificada en UTF8. El problema es que cuando abres la misma web en Internet Explorer 8, este no muestra bien la codificación y esto es porque Internet Explorer 8 no lee bien el charset del meta de Content-Type y por tanto toma la codificación por defecto.

Una nota interesante es que Internet Explorer 8, aunque no reconoce el meta de Content-Type, si que reconoce el encabezado Content-Type que se envia desde el servidor en el protocolo http, por lo que, siguiendo unos sencillos pasos podemos paliar este problema y migrar esos proyectos antiguos que puedan haber por ahí a editores mas modernos.

  1. Recodificar el proyecto. Podemos utilizar un editor que guarde los archivos por defecto en UTF8 o hacernos de algún script que lo haga por nosotros.
  2. Añadir el meta de UTF8. En todas las páginas publicas, debemos añadir un meta de UTF8 y si ya existiera con el típico ISO-8859-1, cambiarlo a UTF8. Algo como <meta http-equiv=»Content-Type» content=»text/html; charset=UTF-8″ />
  3. Por último y no menos importante, devolver el encabezado de http con el Content-Type en UTF-8. Con algo como esto: header(‘Content-type: text/html; charset=utf-8’);

El código resultante sería en nuestro html:

<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <title> Indalcasa</title>
...

Y nuestro php, antes de devolver nada de texto, pintar la cabecera de http:

<?php
  header('Content-type: text/html; charset=utf-8');
?>

Por último comentaros que w3c recomienda en HTML5 que el meta de Content-Type se ponga como el primer elemento dentro del head antes incluso que el title para que se pueda interpretar correctamente la página.

Forzar la descarga de archivos en Apache con .htaccess e IIS con web.config

Cuando tenemos una página web, hay ocasiones en la que queremos que un fichero se descargue, por ejemplo si queremos hacer un sistema de almacenamiento tipo dropbox o simplemente unos pdf que tenemos en nuestra web. Normalmente cuando tenemos un archivo como un pdf, tradicionalmente este siempre se descargaba al abrirlo, pero con los avances en los navegadores, cada día mas potentes, nos dan previsualizadores de pdf, por lo que ya por defecto no se descarga porque los vemos online.

En este ejemplo hablo de pdf, pero se puede llevar a cualquier tipo de archivo, incluso un .html. Un archivo pdf tiene como MIME application/pdf, pero ¿Qué es un MIME? Un MIME es un estándar que define tipos de ficheros en base a una categoría y una subcategoría, por ejemplo, un pdf de tipo application/pdf quiere decir que es un archivo de aplicación del tipo pdf. Otros ejemplos de MIME son de imágenes como el image/jpeg o de texto en html text/html. Cuando un navegador abre un pdf, el servidor tiene por defecto configurado indicar que el archivo es de tipo application/pdf que se indica en la cabecera de http Content-Type.

Tras esta breve explicación, a veces pasa, que no todos los navegadores tienen visores de pdf, por los que algunos como pasa con algunas versiones de Firefox o Internet Explorer se descarguen los archivos, mientras que Safari o Chrome los visualicen online. Pero claro, este comportamiento no es muy elegante ya que quizás sea necesario que se comporte igual en todos los navegadores y tengamos que hacer que siempre se descargue el archivo y esto ya depende del servidor.

Para forzar la descarga de archivos, como el ejemplo que tratamos, un .pdf, tendremos que redefinir un MIME asociado a una extensión, es este caso diremos que es un octet-stream o un stream de bytes (un archivo binario). Al entender el navegador, que el archivo se trata de un archivo binario, no intentara leerlo sino que lo descargará independientemente de la extensión que tenga.

Forzar la descarga de archivos en Apache con .htaccess

Tan solo tenemos que añadir una nueva entrada AddType donde definiremos que los archivos .pdf son binarios y por tanto el navegador los descargará:

AddType application/octet-stream .pdf

Forzar la descarga de archivos en IIS con web.config

Al igual que con apache, deberemos indicar al servidor que el tipo de archivo .pdf es un binario:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <system.webServer>
    <staticContent>
      <mimeMap fileExtension=".pdf" mimeType="application/octet-stream" />
    </staticContent>
  </system.webServer>
</configuration>

Ordenar los bundles de ASP.Net MVC

Si alguna vez habeis intentado hacer un gran proyecto con ASP.Net MVC, vereis que utilizar los bundles para incluir el código tanto javascript (ScriptBundle) como css (StyleBundle) en nuestra página es muy útil ya que nos ofrece diferentes formas de tratar los archivos a incluir, uno de mis favoritos es unificar todo el bundle compilado y minimizado. Todo parece un camino de rosas, poder agrupar todos nuestros css y javascript en una sola linea incluyendo archivos o directorios.

No todo es maravilloso y aunque Microsoft suele hacer las cosas bastante bien, siempre se deja algún extraño por el camino. En este caso el extraño es el orden en que se cargan los archivos en la página usando Bundles. Cuando tenemos multitud de archivos javascript o css por ejemplo, se da el caso en que no todos los archivos se cargan en el orden en que lo incluimos. No se si tiene algo que ver mezclar inclusiones por archivos y directorios a la vez, pero no mantiene el orden igual. Es una locura, puesto que aparentemente todo sigue un orden, pero cuando tenemos una docena de archivos javascript o css, a veces hay alguno que se descoloca. A mi me pasó con backbone y underscore, que como sabreis quien haya trabajado con estas librerias, backbone requiere de underscore y si no esta incluido previamente provoca un error.

Lo primero que tenemos que hacer es crear una nueva clase para hacer el IBundleOrderer.OrderFiles que ordene los bundles, en nuestro caso, realmente no queremos que ordene sino que los muestre en el orden en que los escribimos. La clase en cuestión sería:

public class UserDefinedBundleOrderer : IBundleOrderer {
    public virtual IEnumerable<FileInfo> OrderFiles(BundleContext context, IEnumerable<FileInfo> files) {
        return files;
    }
}

Una vez tengamos nuestra clase IBundleOrderer, solo deberemos indicar a nuestro bundle, bien sea un ScriptBundle, un StyleBundle o un Bundle genérico, que utilice como Orderer nuestra clase que se encarga de la «ordenación» o en este caso, dejar las cosas como están. Lo haremos con el Bundle.Orderer como se muestra en el ejemplo:

myBundle.Orderer = new UserDefinedBundleOrderer();

Y ahora os dejo un ejemplo real sacado directamente de un proyecto propio. Así quedaría todo el archivo completo. Podemos ver como el BundleConfig donde definimos la carga de Bundles, tenemos un StyleBundle que cargará nuestros css, mientras que el ScriptBundle cargará nuestras librerías javascript y es a esta clase donde aplicamos nuestra clase para la ordenación de Bundles.

using System.Collections.Generic;
using System.IO;
using System.Web;
using System.Web.Optimization;

namespace MyWeb {
  public class BundleConfig {
    public static void RegisterBundles(BundleCollection bundles) {
      BundleTable.EnableOptimizations = true;

      bundles.Add(new StyleBundle("~/assets/css").Include(
        "~/Content/css/dark-hive/jquery-ui.css",
        "~/Content/css/bootstrap.css",
        "~/Content/css/site.css"
      ));

      ScriptBundle libs = new ScriptBundle("~/assets/libs");
      libs.Orderer = new UserDefinedBundleOrderer();
      libs.Include(
        "~/Scripts/libs/jquery-1.8.2.js",
        "~/Scripts/libs/jquery-ui-1.8.24.js",
        "~/Scripts/libs/jquery-ui.datepicker.es.js", 
        "~/Scripts/libs/underscore-1.4.1.js",
        "~/Scripts/libs/backbone-0.9.2.js",

      bundles.Add(libs);
    }
  }

  public class UserDefinedBundleOrderer : IBundleOrderer {
    public virtual IEnumerable<FileInfo> OrderFiles(BundleContext context, IEnumerable<FileInfo> files) {
      return files;
    }
  }
}

Espero que os sirva de ayuda.

Scroll al inicio