Categories: Javascript, Metro, Windows 8 Posted by by Leonardo on 04/05/2012 13:07 | Comentarios (0)

Cuando queremos representar datos en nuestras páginas a partir de un template que se repite, lo más recomendable es utilizar un motor de templates como JsRender o JsViews, la pregunta es qué alternativas tenemos en WinJS?

Motor de templates integrado en WinJS

Ya existe un tipo definido para manejar templates dentro de WinJS

new WinJS.Binding.Template();

con él podemos indicar a un ListView cómo renderizar cada ítem, y también podemos generar nuestros templates y utilizarlos sobre un elemento del DOM.

Primer crear el template en HTML

Vamos a crear un proyecto metro en blanco y modificar el HTML por defecto para que se vea así:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Application16</title>

    <!-- WinJS references -->
    <link href="//Microsoft.WinJS.0.6/css/ui-dark.css" rel="stylesheet">
    <script src="//Microsoft.WinJS.0.6/js/base.js"></script>
    <script src="//Microsoft.WinJS.0.6/js/ui.js"></script>

    <!-- Application16 references -->
    <link href="/css/default.css" rel="stylesheet">
    <script src="/js/default.js"></script>
</head>
<body>
<p>
    <div id="template">
        <span data-win-bind="innerText:valor"></span>
    </div>
</p>
    <div id="container"></div>
</body>
</html

La idea es sencilla, utilizar el div con id template como template y renderizarlo dentro del div con id container

Antes que eso vamos a correr la aplicación y ver qué pasa

image

Nada, vemos la pantalla en negro, cosa que en principio está bien, pero vamos a ver el DOM Explorer a ver qué es lo que realmente tiene el navegador en memoria

image

image

Vemos que el div template se está renderizando cosa que no es deseable, sin embargo no nos preocupamos, vamos a default.js y hacemos una pequeña modificación para decirle a WinJS que ese elemento debe ser tratado como un template.

(function () {
    "use strict";

    var app = WinJS.Application;

    app.onactivated = function (eventObject) {
                        WinJS.UI.processAll().then(function () {

                            //recuperamos el elemento del DOM
                            var templateElement = document.querySelector("#template");
                            //creamos un template a partir del elemento recuperado
                            var template = new WinJS.Binding.Template(templateElement);

                        });
    };

    app.start();
})();

Simplemente recuperamos el elemento del DOM y creamos un objeto del tipo WinJS.Binding.Template a partir de él, corremos la aplicación y vemos el DOM Explorer nuevamente

image

Vemos que WinJS agregó al elemento que definimos como template “display: none” para que no se vea en la página, entonces primer tema solucionado.

Renderizando datos a partir del tempalte

Ahora vamos a crear un set de datos y utilizar el template recién definido

(function () {
    "use strict";
    //creamos un objeto con una propiedad
    var data = {valor:"primer valor del set de datos"};

    var app = WinJS.Application;

    app.onactivated = function (eventObject) {
                        WinJS.UI.processAll().then(function () {

                            //recuperamos el elemento del DOM
                            var templateElement = document.querySelector("#template");
                            //creamos un template a partir del elemento recuperado
                            var template = new WinJS.Binding.Template(templateElement);
                            //recuperamos el elemento sobre el cual vamos a renderizar los datos
                            var target = document.querySelector("#container");
                            //renderizamos los datos sobre el destino
                            template.render(data, target);
                        });
    };

    app.start();
})();

creamos una variable en la que colocamos un objeto que será nuestro origen de datos, luego utlizamos la función render del template para utlizar esos datos y renderizar el resultado sobre el destino.

image

Perfecto, qué pasa si queremos renderizar un array de datos, entonces tenermos que usar un bucle del siguiente modo:

(function () {
    "use strict";
    //creamos un objeto con una propiedad
    var data = [{ valor: "primer valor del set de datos" }, { valor: "segundo valor del set de datos" }, { valor: "tercer valor del set de datos" }];

    var app = WinJS.Application;

    app.onactivated = function (eventObject) {
                        WinJS.UI.processAll().then(function () {

                            //recuperamos el elemento del DOM
                            var templateElement = document.querySelector("#template");
                            //creamos un template a partir del elemento recuperado
                            var template = new WinJS.Binding.Template(templateElement);
                            //recuperamos el elemento sobre el cual vamos a renderizar los datos
                            var target = document.querySelector("#container");

                            //renderizamos los datos sobre el destino
                            for (var i = 0; i < data.length; i++) {
                                template.render(data[i], target);
                            }                                                       
                        });
    };

    app.start();
})();

y con eso solucionamos el problema, o podemos utilizar la función foreach que agrega WinJS a los array, del siguiente modo:

(function () {
    "use strict";
    //creamos un objeto con una propiedad
    var data = [{ valor: "primer valor del set de datos" }, { valor: "segundo valor del set de datos" }, { valor: "tercer valor del set de datos" }];

    var app = WinJS.Application;

    app.onactivated = function (eventObject) {
                        WinJS.UI.processAll().then(function () {

                            //recuperamos el elemento del DOM
                            var templateElement = document.querySelector("#template");
                            //creamos un template a partir del elemento recuperado
                            var template = new WinJS.Binding.Template(templateElement);
                            //recuperamos el elemento sobre el cual vamos a renderizar los datos
                            var target = document.querySelector("#container");

                            //renderizamos los datos sobre el destino
                            data.forEach(function (item) {
                                template.render(item, target);
                            });
                        });
    };

    app.start();
})();

y listo:

image

Utilizando templates remotos

Una opción interesante es la capacidad de poder utilizar un template procedente de otro archivo, esto es bien sencillo, creamos un nuevo archivo template.html y colocamos todo el contenido de nuestro template dentro.

<div id="template">
    <p>
        <span data-win-bind="innerText:valor" ></span>
    </p>
</div>

y modificamos el código javascript para utilizar el template externo

(function () {
    "use strict";
    //creamos un objeto con una propiedad
    var data = [{ valor: "primer valor del set de datos" }, { valor: "segundo valor del set de datos" }];

    var app = WinJS.Application;

    app.onactivated = function (eventObject) {
                        WinJS.UI.processAll().then(function () {

                            //recuperamos el elemento del DOM
                            var templateElement = document.querySelector("#template");
                            //creamos un template a partir del archio remoto
                            var template = new WinJS.Binding.Template(null, { href: "/html/template.html" });

                            //recuperamos el elemento sobre el cual vamos a renderizar los datos
                            var target = document.querySelector("#container");

                            //renderizamos los datos sobre el destino
                            data.forEach(function (item) {
                                template.render(item, target);
                            });
                        });
    };

    app.start();
})();

simplemente cuando creamos el objeto template le indicamos el parámetro href apuntando al archivo externo, el resto es igual.

Hasta la próxima.

Categories: Javascript, MUG, Windows 8 Posted by by Leonardo on 29/04/2012 19:05 | Comentarios (0)

Una buena costumbre al desarrollar aplicaciones metro con Javascript es tener nuestro código en módulos separados utilizando funciones autoejecutables y así tener un scope acotado.

Existe un pequeño problema, al ser un archivo separado no tenemos intellisense de las librerías de WinJS (ni de ningúna otra), algo así:

image

Este problema se incrementa ya que no tenemos referencias físicas en el proyecto de los archivos base de WinJS, sino que accedemos a ellos a través de un path especial

image

Sin embargo podemos hacer algo para tener intellisense del WinJS en nuestro módulo del siguiente modo:

image

Simplemente agregamos los tags reference con el atributo path apuntando a los path especiales de las librerías de WinJS y tenemos intellisense.

Hasta la próxima.

Categories: Alt.Net, Javascript, Metro, Windows 8 Posted by by Leonardo on 02/04/2012 8:13 | Comentarios (2)

UPDATE 2012.04.12
Se aplazó la VAN para el Viernes 27 de Abril a las 18:00hs GMT

Windows 8 logo

Una vez más voy a tener la suerte de participar de una VAN (Virtual Alt.Net), para la comunidad de Alt.Net Hispano, en esta oportunidad será sobre Aplicaciones Metro con Javascript.

VW Camper

El tema

La idea hablar de Windows 8 y las aplicaciones metro, comenzando con un poco de teoría sobre la plataforma y qué son y cuáles los lineamientos de las aplicaciones Metro.

Vamos a tratar de ir contando un poco el modelo de programación y cómo nos ayuda WinJS para todo esto

No voy a estar sólo

En la VAN me va a acompañar Rubén Altman del equipo de Kinetica y tal vez algunas sorpresas.

La fecha

Será el viernes 13 de Abril a las 18hs GMT, las 15hs de Argentina.

Cómo asistir?

Para participar de la VAN es necesario tener instalado Microsoft Office Live Meeting 2007 client

El Link de acceso es acá

Pueden dejar comentarios e inquietudes en la lista de correo

Para más información acerca de las VAN pueden acceder acá

Entonces, hecha la invitación nos vemos el 13.

Leonardo.

Categories: ASP.NET MVC, C#, MUG Posted by by Leonardo on 21/03/2012 10:48 | Comentarios (0)

mvc_DesignPattern

Durante las primeras semanas de Abril estaré en el Grupo de Usuarios de Microsoft dando un nuevo taller sobre MVC, en este caso actualizado a la versión 3 y hablando un poco sobre las novedades de MVC 4.

Contenido

Por supuesto que debido a la cantidad de temas y la duración del taller el nivel será introductorio, la idea es dar un repaso sobre los conceptos más sobresalientes y no dejar de nombrar algunos detalles de implementación en situaciones reales, el temario será el siguiente:

Introducción a MVC

  • Teoría
  • Separación de conceptos
  • Escalabilidad
  • Implementaciones: ASP.NET MVC, Monorail.
  • Diferencias entre MVC y WebForms / ASP clásico
  • Colaboración
  • Legibilidad
  • Testeo
  • Por qué necesitamos MVC?

Ruteo

  • Modelos de ruteo
  • SEO
  • Indización de buscadores
  • Configuración personalizada
  • Áreas

El modelo

  • Responsabilidad del modelo
  • Separación
  • Testeo

El controlador

  • Responsabilidades
  • Action Filters
  • Model Binders
  • ActionResult
  • FileResult
  • JSONResult
  • JavaScriptResult

La vista

  • Responsabilidad
  • Motores de renderización
    • ASP.NET
    • Razor
  • HTMLHelpers
  • ViewData
  • Vistas tipadas
  • Validaciones
  • Testeo

Aspectos avanzados

  • Ajax
  • jQuery y MVC
  • MVC 4
  • Mejoras en Razor
  • ASP.NET Web API

El taller se desarrollará íntegramente con PC para cada asistente para poder experimentar con los conceptos que vamos repasando.

Fechas y duración

Serán cuatro encuentros:

  • Lunes 16 de Abril de 2012
  • Martes 17
  • Lunes 23
  • Martes 24

el horarios será de 18:00 a 22:00 hs. El lugar es en Rivadavia 1479 - Piso 1 - Oficina A 
Ciudad Autónoma de Bs.As.

Registración

Dejo el link para registrarse, el cupo es limitado y es con costo.

Nos vemos, Leonardo.

Categories: Javascript, Windows 8, Metro Posted by by Leonardo on 20/03/2012 19:48 | Comentarios (0)

En el post anterior sobre Metro vimos cómo se puede hacer para “bindear” el modelo a la vista, existe una particularidad en el ejemplo que pasó casi inadvertida y tiene que ver con la forma de organizar las aplicaciones Metro Javascript.

El patrón module en Javascript

En este post vimos cómo se pueden utilizar funciones auto-ejecutable para tener un ámbito de visibilidad limitado y que nuestro código no interfiera con el resto de la aplicación, la utilización de esta técnica se conoce como el patrón module.

El inconveniente de utilizarlo es que lo que definamos dentro de la función no podrás ser visto desde afuera, es por eso que yo definí el modelo como una propiedad del objeto document el cual es visible globalmente, sin embargo, existe una alternativa a esto.

Namespaces en Metro

Podemos crear un objeto global y generar un espacio de nombres y allí poner los orígenes de datos, ya vimos cómo podemos hacerlo, sin embargo WinJS ya trae consigo un mecanismo para crear espacios de nombre.

La idea es definir dentro de un módulo nuestro origen de datos pero dentro de un espacio de nombres que pueda ser visto globalmente para poder referenciarlo desde la vista con su nombre completo y el WinJS se ocupe del binding. Entonces manos a la obra>

Un ejemplo vale más de mil palabras

Creamos una nueva aplicación Metro de Javascript y vamos directo al archivo default.js

// For an introduction to the Blank template, see the following documentation:
// http://go.microsoft.com/fwlink/?LinkId=232509
(function () {
    "use strict";

    var app = WinJS.Application;

    app.onactivated = function (eventObject) {
        if (eventObject.detail.kind === Windows.ApplicationModel.Activation.ActivationKind.launch) {
            if (eventObject.detail.previousExecutionState !== Windows.ApplicationModel.Activation.ApplicationExecutionState.terminated) {
                // TODO: This application has been newly launched. Initialize 
                // your application here.
            } else {
                // TODO: This application has been reactivated from suspension. 
                // Restore application state here.
            }
            WinJS.UI.processAll();
        }
    };

    app.oncheckpoint = function (eventObject) {
        // TODO: This application is about to be suspended. Save any state
        // that needs to persist across suspensions here. You might use the 
        // WinJS.Application.sessionState object, which is automatically
        // saved and restored across suspension. If you need to complete an
        // asynchronous operation before your application is suspended, call
        // eventObject.setPromise(). 
    };

    app.start();
})();

Inicialmente tenemos esto, vamos a generar un origen de datos y colocarlo como propiedad del objeto document

(function () {
    "use strict";
    //creamos el origen de datos
    var dataSource = { nombre: 'leonardo', apellido: 'micheloni' };
    //lo asignamos como una propiedad del document para tener visibilidad fuera de
    //este módulo
    document.dataSource = dataSource;

    var app = WinJS.Application;

    app.onactivated = function (eventObject) {
        //le decimos a WinJS que ejecute los binding
        WinJS.Binding.processAll();
    };

    app.start();
})();

Modificamos el HTML

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Application16</title>

    <!-- WinJS references -->
    <link href="//Microsoft.WinJS.0.6/css/ui-dark.css" rel="stylesheet">
    <script src="//Microsoft.WinJS.0.6/js/base.js"></script>
    <script src="//Microsoft.WinJS.0.6/js/ui.js"></script>

    <!-- Application16 references -->
    <link href="/css/default.css" rel="stylesheet">
    <script src="/js/default.js"></script>
</head>
<body>
    <div data-win-bindsource="document.dataSource">
        <p data-win-bind="innerText: nombre"></p>
        <p  data-win-bind="innerText: apellido"></p>
    </div>
</body>
</html>

y funciona muy bien

image

Utilizando la función namespace de WinJS

Ahora, vamos a poner nuestro origen de datos dentro de otro archivo js y agregarlo a un namespace y luego referenciarlo desde la vista

default.js

(function () {
    "use strict";
    var app = WinJS.Application;

    app.onactivated = function (eventObject) {
        //le decimos a WinJS que ejecute los binding
        WinJS.Binding.processAll();
    };
    
    app.start();
})();

data.js

(function () {
    'use strict';
    //creamos el origen de datos
    var dataSource = { nombre: 'leonardo', apellido: 'micheloni' };
    //definimos el namespace "datos" y colocamos nuestro origen dentro
    WinJS.Namespace.define("datos", dataSource);
})();

default.html

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Application16</title>

    <!-- WinJS references -->
    <link href="//Microsoft.WinJS.0.6/css/ui-dark.css" rel="stylesheet">
    <script src="//Microsoft.WinJS.0.6/js/base.js"></script>
    <script src="//Microsoft.WinJS.0.6/js/ui.js"></script>

    <link href="/css/default.css" rel="stylesheet">
    <script src="/js/default.js"></script>
    <!-- referenciamos el archivo donde definimos nuestro origen de datos-->
    <script src="/js/data.js"></script>
</head>
<body>
    <div data-win-bindsource="document.dataSource">
        <p data-win-bind="innerText: nombre"></p>
        <p  data-win-bind="innerText: apellido"></p>
    </div>
</body>
</html>

y el resultado es igual al anterior

image

De más está decir que son pocas las situaciones en la cuales no deberíamos colocar nuestro código en un módulo separado y referenciarlo utilizando namespaces.

Hasta la próxima, Leonardo.

Categories: Javascript Posted by by Leonardo on 18/03/2012 16:00 | Comentarios (4)

Si bien Javascript no provee un mecanismo propio para la creación de espacios de nombres es posible lograr este tipo de organización de nuestro código con algunas técnicas.

Primer intento

Podemos crear un objeto y utilizarlo como raíz del namespace y agregar los objetos que queramos dentro del namespace como propiedades de la raíz.

<html>
<head>
<script type="text/javascript">
	//creamos nuestro namespace
	var MiEspacio = {};
	//agregamos un objeto "dentro" del namespace
	MiEspacio.Objeto1 = function(){};
	//entonces podemos hacer
	MiEspacio.Objeto1.propiedad1 = "hola";
	//y comprobamos que se pueda leer la propiedad
	console.log(MiEspacio.Objeto1.propiedad1);
</script>
</head>
<body></body>
</html>

podemos repetir esto para tener un namespace con la cantidad de niveles que nos sea necesario

<html>
<head>
<script type="text/javascript">
	//creamos nuestro namespace
	var MiEspacio = {};
	//agregamos un objeto "dentro" del namespace y del módulo
	MiEspacio.modulo1.Objeto1 = function(){};
	//entonces podemos hacer
	MiEspacio.modulo1.Objeto1.propiedad1 = "hola";
	//y comprobamos que se pueda leer la propiedad
	console.log(MiEspacio.modulo1.Objeto1.propiedad1);
</script>
</head>
<body></body>
</html>

por supuesto que para tenet una visibilidad alta deberíamos definir el objeto raíz lo más global posible.

Creando una función para generar namespaces

Vamos a crear una función que nos permita generar namespaces de una manera sencilla y mejor aún, que verifique que no se creen dos veces los mismo, o peor aún, que se sobre-escriban elementos ya definidos.

La idea es hacer algo así:

UTIL = {}; //definimos el objeto UTIL
RAIZ = {}; //definimos el objeto raíz
UTIL.namespace = function (expression, members){
		//dividimos la expresion en cada punto 
		var partes = expression.split('.');
		//el objeto raíz es el primer elemento
		var raiz = eval(partes[0]);
		//quitamos de la expresion la raíz
		partes = partes.slice(1);
		for(i = 0;i < partes.length;i++){		
			//en caso que no está ya definido es módulo
			//se crea
			if(typeof raiz[partes[i]] === 'undefined'){
				//es el último elemento agregamos los miembros
				if(i==partes.length-1){
					raiz[partes[i]] = members;
				}else{
					raiz[partes[i]] = {};
				}
			}
		}
};

UTIL.namespace("RAIZ.pp", {objeto1: {propiedad1: "valor"}});

y listo

image

por supuesto que esta función es muy mejorable, por ejemplo podría crear el objeto raíz si no existe, pero sirve de ejemplo.

Nos leemos.

Leonardo.

Categories: Javascript, Windows 8, Metro Posted by by Leonardo on 15/03/2012 14:41 | Comentarios (0)

Las aplicaciones metro con Javascript utilizar al patron MVVM, WinJS se encarga de gestionar el funcionamiento del mismo.

Model View-View Model

La idea de este patrón es tener un modelo asociado a una vista de la aplicación, es decir: si tenemos una vista que muestra un listado de productos ese listado es el modelo de la vista, cuando para nuestro dominio el producto es parte del modelo y el listado no existe como tal.

Algo adicional es que la asociación del modelo de la vista con los elementos de la vista (en este caso elementos HTML) es automática y no tenemos que poner sintaxis tipo MVC y llamadas a helpers para que se muestren los datos sino que es un mecanismo más transparente

Atributos data-algo

HTML5 permite definir como válidos atributos para cualquier elemento con el nombre data-nombre-subnombre para utilizarlos de manera personalizada, justamente WinJS se basa en esto para asociar el modelo con la la vista.

Manos a la obra

Vamos a hacer una aplicación Metro bien sencilla para demostrar cómo funciona esto, vamos al Visual Studio y creamos una aplicación Metro Javascript en blanco.

image

La aplicación es bien simple

Vamos al archivo default.js y vemos lo siguiente

// For an introduction to the Blank template, see the following documentation:
// http://go.microsoft.com/fwlink/?LinkId=232509
(function () {
    "use strict";

    var app = WinJS.Application;

    app.onactivated = function (eventObject) {
        if (eventObject.detail.kind === Windows.ApplicationModel.Activation.ActivationKind.launch) {
            if (eventObject.detail.previousExecutionState !== Windows.ApplicationModel.Activation.ApplicationExecutionState.terminated) {
                // TODO: This application has been newly launched. Initialize 
                // your application here.
            } else {
                // TODO: This application has been reactivated from suspension. 
                // Restore application state here.
            }
            WinJS.UI.processAll();
        }
    };

    app.oncheckpoint = function (eventObject) {
        // TODO: This application is about to be suspended. Save any state
        // that needs to persist across suspensions here. You might use the 
        // WinJS.Application.sessionState object, which is automatically
        // saved and restored across suspension. If you need to complete an
        // asynchronous operation before your application is suspended, call
        // eventObject.setPromise(). 
    };

    app.start();
})();

En principio este js no tiene más que los eventos de la aplicación asociados, vamos a modificar levemente el código para crear un objeto que será nuestro modelo y pasarlo a la vista

(function () {
    "use strict";

    var app = WinJS.Application;

    //colocamos nuestro código para que se ejecute cuando la aplicación se activa
    app.onactivated = function (eventObject) {
        //creamos nuestro modelo
        var modelo = { nombre: "leonardo", apellido: "micheloni" };
        //le decimos a WinJS que procese los bindings y le especificamos cuál
        //el primer parámetro es el objeto DOM desde el cual se ejecuta el binding
        //en caso de poner null se utiliza document
        //el segundo parámetro es el modelo
        WinJS.Binding.processAll(null, modelo); 
    };

    //iniciamos la aplicación
    app.start();
})();

con eso alcanza, quité el código que no va a hacer falta, y agregué dos líneas, en la primer defino un objeto que será el modelo, con WinJS.Binding.processAll ejecutamos los bindings, ahora vamos a modificar la vista, que en principio es así:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>ejemploBinding</title>

    <!-- WinJS references -->
    <link href="//Microsoft.WinJS.0.6/css/ui-dark.css" rel="stylesheet">
    <script src="//Microsoft.WinJS.0.6/js/base.js"></script>
    <script src="//Microsoft.WinJS.0.6/js/ui.js"></script>

    <!-- ejemploBinding references -->
    <link href="/css/default.css" rel="stylesheet">
    <script src="/js/default.js"></script>
</head>
<body>
    <p>Content goes here</p>
</body>
</html>

la modificamos para que quede así:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>ejemploBinding</title>

    <!-- WinJS references -->
    <link href="//Microsoft.WinJS.0.6/css/ui-dark.css" rel="stylesheet">
    <script src="//Microsoft.WinJS.0.6/js/base.js"></script>
    <script src="//Microsoft.WinJS.0.6/js/ui.js"></script>

    <!-- ejemploBinding references -->
    <link href="/css/default.css" rel="stylesheet">
    <script src="/js/default.js"></script>
</head>
<body>
    <div>
        <p data-win-bind="innerText: nombre"></p>
        <p data-win-bind="innerText: apellido"></p>
    </div>
</body>
</html>

la propiedad data-win-bind permite indicar una propiedad del elemento HTML (en este caso innerText) y asignarlo un valor proveniente desde el modelo, en este caso nombre, el resultado

image

Un poco más declarativo

Podemos hacer lo mismo sin necesidad de indicar por código la asociación del modelo con la vista, modificamos primero el js

(function () {
    "use strict";

    var app = WinJS.Application;
    //colocamos nuestro modelo en el document, para poder verlo globalmente
    document.modelo = { nombre: "leonardo", apellido: "micheloni" };

    app.onactivated = function (eventObject) {
        WinJS.Binding.processAll(); 
    };

    app.start();
})();

Simplemente declaramos el modelo como una propiedad del objeto document, no es lo más feliz tal vez sin embargo sin usar namespaces (que será tema de otro post) es lo que tenemos más a mano. Vamos a ver la vista.

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>ejemploBinding</title>

    <!-- WinJS references -->
    <link href="//Microsoft.WinJS.0.6/css/ui-dark.css" rel="stylesheet">
    <script src="//Microsoft.WinJS.0.6/js/base.js"></script>
    <script src="//Microsoft.WinJS.0.6/js/ui.js"></script>

    <!-- ejemploBinding references -->
    <link href="/css/default.css" rel="stylesheet">
    <script src="/js/default.js"></script>
</head>
<body>
    <div data-win-bindsource="document.modelo" >
        <p data-win-bind="innerText: nombre"></p>
        <p data-win-bind="innerText: apellido"></p>
    </div>
</body>
</html>

nótese que en declaramos el data-win-bindsource en el div que contiene a los párrafos, esta es otro de las propiedades de WinJS para manipular los datos de la vista.

Lo ejecutamos y obtenemos el mismo resultado

image

Mágico, nos leemos.

Leonardo.

Categories: ASP.NET MVC, Javascript, jQuery Posted by by Leonardo on 12/03/2012 8:54 | Comentarios (0)

Ya hemos hablado acerca de cómo subir archivos con ASP.NET MVC y algunos detalles sobre formularios y demás, en esta oportunidad vamos a ver cómo podemos lograr lo mismo pero usando AJAX.

Primero el lado del cliente

Del lado del cliente podemos acceder al objeto File controlado por el usuario, simplemente haciendo esto (usando jQuery claro)

$(function () {
    var file = $("#file1");
}); 

Uno pensaría (como lo hacía yo hasta éste momento) que no es posible acceder a un archivo desde Javascript, sin embargo el sandbox no nos impide acceder a un objeto creado y manejado por el usuario, por lo tanto podemos acceder al contenido del archivos una vez que el usuario lo haya seleccionado, modificamos el código para tal fin

<!DOCTYPE html>

<html>
<head>
    <title>Index</title>
    <script src="@Url.Content("~/Scripts/jquery-1.5.1.js")" type="text/javascript"></script>
    <script type="text/javascript">
        $(function () {
            $("#frm :submit").click(function () {
                var file = $("#file1").get();
                return false;
            });
        });    
    </script>
</head>
<body>
    <div>
        <form id="frm" action="@Url.Action("Upload")" method="post">
            <input type="file" id="file1" />
            <input type="submit" value="enviar" />
        </form>
    </div>
</body>
</html>

Entonces atrapamos el evento click del botón submit del formulario para que cuando se ejecute recuperar el objeto File, retornamos false para que no se ejecute el evento por defecto, es decir, que no se envíe el formulario.

Enviando el contenido del objeto File por Ajax

Para enviar el contenido del File por Ajax vamos a tener que utilizar el la función AJAX de jQuery directamente ya que necesitamos más control que el que nos brinda $.post. para lograrlo modificamos el código nuevamente.

        $(function () {
            $("#frm :submit").click(function () {
                var file = $("#file1").get();
                $.ajax({
                    type: 'POST',
                    url: $("#frm").attr("action"),
                    data: file,
                    dataType: "application/json",
                    contentType: "application/octet-stream"
                });
                return false;
            });
        });

nótese que ponemos en el Content-Type el tipo octet-stream para poder enviar el archivo como un stream y leerlo fácilmente desde C#

Leyendo el contenido desde ASP.NET MVC

Lo que hacemos es enviar por AJAX utilizando POST el contenido del objeto File, esto está muy bien, vamos a ver cómo lo recibimos desde el lado de ASP.NET MVC

using System.Web.Mvc;
using System.IO;
using System.Text;

namespace MvcApplication6.Controllers
{
    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            return View();
        }

        public ActionResult Upload()
        {
            var file = new StreamReader(this.HttpContext.Request.InputStream, Encoding.UTF8).ReadToEnd();
            return Json(new { contenidoRecibido = file });
        }
    }
}

Para leer el archivo enviado tenemos que acceder el InputStream ya que estamos enviando el contenido como parte del cuerpo de HTTP. Un detalle más es que por defecto viaja con Encoding UTF-8 es por eso que para leerlo tenemos que especificarlo en el StreamReader. Por supuesto que este ejemplo sirve sólo para archivos de texto, no es difícil modificarlo para cualquier tipo de contenido.

image

y listo, faltaría poner código del lado del cliente para que ante la respuesta correcta (no la que puse que es un ejemplo) de feedback al usuario.

Nos leemos, Leonardo.

Categories: Visual Studio Posted by by Leonardo on 03/03/2012 19:25 | Comentarios (0)

Desde el pasado 29 de Febrero ya se encuentra disponible para la descarga la versión 11 de Visual Studio. Así que vamos a ver un poquito de lo que trae.

Instalación

Podemos instalar Visual Studio 11 en Windows 7, o en Windows 8 Consumer Preview que se puede descargar desde acá, yo instalé la versión Professional en Windows 7 si bien se pueden descargar varias versiones incluso la Ultimate

image

y luego de un buen rato (en mi PC un i5 con 8GB 10min)

image

Iniciamos Visual Studio 11 y ya vemos cambios

Una pequeña vuelta por las novedades de la IDE

Muchas cosas han cambiado en la IDE, vamos a ver algunas de ellas para ir saboreando lo que se viene en nuestra herramienta de todos los días

image

Cambios en el aspecto

image

Lo que vemos es un diseño totalmente renovado que incluye el aspecto, la iconografía y la ubicación de los comandos

Compatibilidad hacia atrás

Hay muy buenas noticias, con Visual Studio 11 podemos crear proyectos para cualquier framework desde el 2.0 en adelante

image

Y las aplicaciones Metro?

Bien, si no estamos en Windows 8 no tenemos la posibilidad de crear Metro Apps si estamos en Windows 8 por supuesto que si.

Mejoras en el Solution Explorer

Dentro del Solution explorer tenemos varias consultas sobre los elementos que forman parte de nuestra solución

Búsqueda por nombre

image

Tipos base y tipos derivados

image

image

image

Miembros

image

Desde acá también podemos navegar directo al código del miembro seleccionando el botón Preview Selected Item

image

Múltiples instancias del Solution Explorer

A partir de cualquier elemento podemos crear una nueva vista del Solution Explorer y tener múltiples instancias con el comando New View

image

O directamente posicionar la vista a partir de un punto con el comando Scope To This

image

image

Page Inspector

Si trabajamos en Web cuando queramos ejecutar nuestra aplicación tenemos dos novedades, primero poder seleccionar el navegador directo desde la barra de herramientas por defecto

image

La otra es una herramienta nueva, el Page Inspector, una suerte de Internet Explorer Developer Toolbar, Firebug o Dragonfly si prefieren, integrado en Visual Studio

image

Bueno, como un primer vistazo es más que interesante, hay muchas cosas más con relación a la navegación de elementos y la forma de manejar los tabs abiertos, además la versión Professional tiene muchas más herramientas que antes, por ejemplo:

  • Nueva ventana Unit Test Explorer
  • Métricas de código
  • Nuget
  • Análisis de código (Stylecop)
  • Análisis de performance / profiler

En definitiva, muchas de las cosas que eran exclusivas de la versión Ultimate un poco cara para los programadores de a pié como un servidor Sonrisa en la versión Professional.

Hasta la próxima, Leonardo.

Categories: ASP.NET MVC, C#, Web API Posted by by Leonardo on 19/02/2012 14:55 | Comentarios (4)

ASP.NET Web API es una nueva capacidad agregada a ASP.NET MVC desde el developer preview de ASP.NET MVC 4, la idea es proveer un mecanismo pensado desde el principio para cubrir todas las interacciones no-humanas de nuestra aplicación, esto es principalmente llamadas Ajax.

Comenzando a usar la Web API

Una vez instalada la developer preview de MVC 4 tenemos el template de Web API disponible en Visual Studio 2011.

Abrimos Visual Studio y seleccionamos el template de MVC 4

image

 

Y ahora viene la parte diferentes, nos aparecen otras opciones desde la cuales podemos crear un proyecto Web API

image

Perfecto, una vez hecho esto tenemos la estructura del proyecto creada, si miramos lo que genera el template por defecto vemos que existen dos controladores:

  • HomeController
  • ValuesController

image

HomeController es un controlador tal cual lo conocemos

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace webApiTest.Controllers
{
    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            return View();
        }
    }
}

Hasta ahora nada raro, sin embargo si vemos el código del ValuesController nos encontramos con algunas novedades

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Web.Http;

namespace webApiTest.Controllers
{
    public class ValuesController : ApiController
    {
        // GET /api/values
        public IEnumerable Get()
        {
            return new string[] { "value1", "value2" };
        }

        // GET /api/values/5
        public string Get(int id)
        {
            return "value";
        }

        // POST /api/values
        public void Post(string value)
        {
        }

        // PUT /api/values/5
        public void Put(int id, string value)
        {
        }

        // DELETE /api/values/5
        public void Delete(int id)
        {
        }
    }
}

Vemos dos cosas que nos llaman la atención

  • Hereda de ApiController en lugar de Controller
  • Las acciones retornan void o string el lugar de ActionResult

Otra cosa no menos es que las acciones tiene nombres REST-friendly como:

  • Get
  • Post
  • Put
  • Delete

Vamos a ver qué dice el global.asax sobre las rutas

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Http;
using System.Web.Mvc;
using System.Web.Optimization;
using System.Web.Routing;

namespace webApiTest
{
    // Note: For instructions on enabling IIS6 or IIS7 classic mode, 
    // visit http://go.microsoft.com/?LinkId=9394801

    public class WebApiApplication : System.Web.HttpApplication
    {
        public static void RegisterGlobalFilters(GlobalFilterCollection filters)
        {
            filters.Add(new HandleErrorAttribute());
        }

        public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

            routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );

            routes.MapRoute(
                name: "Default",
                url: "{controller}/{action}/{id}",
                defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
            );
        }

        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();

            RegisterGlobalFilters(GlobalFilters.Filters);
            RegisterRoutes(RouteTable.Routes);

            BundleTable.Bundles.RegisterTemplateBundles();
        }
    }
}

Además de la clásica ruta Controlador/Acción, vemos una ruta (antes de ésta) que dice:

routes.MapHttpRoute(
    name: "DefaultApi",
    routeTemplate: "api/{controller}/{id}",
    defaults: new { id = RouteParameter.Optional }
);

justamente en MapHttpRoute está la magia, pero qué es lo qué nos permite hacer

  • Consultas tipo REST, relacionando los nombres de las acciones con los verbos HTTP
  • Negociar, tipos de retorno, por ejemplo a través de headers HTTP nos devuelve XML o JSON

Vamos a modificar un poco el código por defecto para ver si esto es verdad

using System.Collections.Generic;
using System.Linq;
using System.Web.Http;

namespace webApiTest.Controllers
{
    public class ValuesController : ApiController
    {
        private static Dictionary values;

        public ValuesController()
        {
            if (values == null)
            {
                values = new Dictionary();
                values.Add(1, "valor1");
                values.Add(2, "valor2");
            }
        }

        public IDictionary Get()
        {
            return values;
        }

        public string Get(int id)
        {
            return values.First(v => v.Key == id).Value;
        }

        public void Post(string value)
        {
            var newId = ((int)values.Max(i=>i.Key)+1);
            this.Put(newId, value);
        }

        public void Put(int id, string value)
        {
            this.Delete(id);
            values.Add(id, value);
        }

        public void Delete(int id)
        {
            values.Remove(id);
        }
    }
}

Perfecto modificamos un poco para tener todos los verbos REST implementados y que funcione como debe, ahora para probarlo vamos al Fiddler, primer prueba

GET

En la solapa de “Compose” creamos un request utilizando GET a la Url configurada en global.asax

image

HTTP/1.1 200 OK
Server: ASP.NET Development Server/10.0.0.0
Date: Sun, 19 Feb 2012 14:27:08 GMT
X-AspNet-Version: 4.0.30319
Cache-Control: no-cache
Pragma: no-cache
Expires: -1
Content-Type: application/json; charset=utf-8
Connection: Close
Content-Length: 89

[{"Key":1,"Value":"valor1"},{"Key":3,"Value":"valor3"},{"Key":4,"Value":"valor por put"}]

Nos responde como esperamos, en formato JSON, esto es porque pusimos (a propósito) el header Content-Type:application/json, podemos “negociar” que nos devuelve XML sin mucho esfuerzo, cambiando simplemente este header

Negociando el tipo de respuesta

Cambiamos en nuestro request el Content-Type y voilá

HTTP/1.1 200 OK
Server: ASP.NET Development Server/10.0.0.0
Date: Sun, 19 Feb 2012 16:23:05 GMT
X-AspNet-Version: 4.0.30319
Cache-Control: no-cache
Pragma: no-cache
Expires: -1
Content-Type: text/xml; charset=utf-8
Connection: Close
Content-Length: 262

<?xml version="1.0" encoding="utf-8"?><....

Mapeo de verbos automático

Bien, en nuestro código cambiamos las acciones para que funcionen como si fuera un ABM, asociamos cada acción de controlador con alta, baja, modificación, consulta..veámos

Consulta con GET

GET http://localhost:51757/api/values HTTP/1.1
Host: localhost:51757
Content-Type: application/json

Respuesta

HTTP/1.1 200 OK
Server: ASP.NET Development Server/10.0.0.0
Date: Sun, 19 Feb 2012 16:43:59 GMT
X-AspNet-Version: 4.0.30319
Cache-Control: no-cache
Pragma: no-cache
Expires: -1
Content-Type: application/json; charset=utf-8
Connection: Close
Content-Length: 55

[{"Key":1,"Value":"valor1"},{"Key":2,"Value":"valor2"}]

Agregar con POST

POST http://localhost:51757/api/values HTTP/1.1
Host: localhost:51757
Content-Type: application/json
Content-Length: 44

{"id":"4","value":"valor agregado con POST"}

Y volvemos a consultar

HTTP/1.1 200 OK
Server: ASP.NET Development Server/10.0.0.0
Date: Sun, 19 Feb 2012 18:17:42 GMT
X-AspNet-Version: 4.0.30319
Cache-Control: no-cache
Pragma: no-cache
Expires: -1
Content-Type: application/json; charset=utf-8
Connection: Close
Content-Length: 99

[{"Key":1,"Value":"valor1"},{"Key":2,"Value":"valor2"},{"Key":3,"Value":"valor agregado con POST"}]

Modificación con PUT

PUT http://localhost:51757/api/values HTTP/1.1
Host: localhost:51757
Content-Type: application/json
Content-Length: 52

{"id":"2","value":"el elemeto 2 modificado con PUT"}

Volvemos a consultar

HTTP/1.1 200 OK
Server: ASP.NET Development Server/10.0.0.0
Date: Sun, 19 Feb 2012 18:20:44 GMT
X-AspNet-Version: 4.0.30319
Cache-Control: no-cache
Pragma: no-cache
Expires: -1
Content-Type: application/json; charset=utf-8
Connection: Close
Content-Length: 124

[{"Key":1,"Value":"valor1"},{"Key":2,"Value":"el elemeto 2 modificado con PUT"},{"Key":3,"Value":"valor agregado con POST"}]

y por último borrar con DELETE

DELETE http://localhost:51757/api/values/1 HTTP/1.1
Host: localhost:51757
Content-Type: application/json

y el resultado

HTTP/1.1 200 OK
Server: ASP.NET Development Server/10.0.0.0
Date: Sun, 19 Feb 2012 19:42:16 GMT
X-AspNet-Version: 4.0.30319
Cache-Control: no-cache
Pragma: no-cache
Expires: -1
Content-Type: application/json; charset=utf-8
Connection: Close
Content-Length: 97

[{"Key":2,"Value":"el elemeto 2 modificado con PUT"},{"Key":3,"Value":"valor agregado con POST"}]

Notemos que en el caso de DELETE al no soportar enviar contenido en el body se utiliza la URL para identificar el recurso, como se declaró en la ruta del global.asax.

routes.MapHttpRoute(
    name: "DefaultApi",
    routeTemplate: "api/{controller}/{id}",
    defaults: new { id = RouteParameter.Optional }
);

En resumen, ASP.NET Web API nos brinda un mecanismo para las llamas que no tiene un resultado visible para el humano, y nos evitar poner en nuestros controladores estos métodos o hacer un servicio WCF con WebHttpBinding, además está pensada para trabajar como REST.

La próxima vez vamos a ver cómo integrar esto con jQuery, hasta entonces.