miércoles, 23 de mayo de 2012

Introducción a Knockout (V) - Cargando datos dinámicamente con ASP.NET MVC4 WebApi

Hasta ahora hemos visto varios ejemplo con knockout pero usando datos estáticos. En este artículo haremos uso de datos "dinámicos" cargados a través de Web Api (una de las funcionalidades que nos ha traído la beta de ASP.NET MVC4).

No es el objetivo de este artículo hacer explicar el funcionamiento de esta nueva funcionalidad, pero para nuestro ejemplo nos basta con lo siguiente
Controller
public class BookController : ApiController
{
  private IBookRepository repository = new BookRepository();

  // GET /api/book
  public IEnumerable<book> Get(string search)
  {
    return (string.IsNullOrEmpty(search) ? repository.Get() : repository.GetList(search));
  }
}
BookRepository
public class BookRepository : IBookRepository
{
  private List<book> books = new List<book>(); 

  public BookRepository()
  {
    Add(new Book
    {
      Id = 1,
      Title = "Introducing Microsoft Sql Server 2012"
    });
    Add(new Book
    {
      Id = 2,
      Title = "Introducing Windows Server 2008 R2"
    });
    Add(new Book
    {
      Id = 3,
      Title = "Visual Studio 2010"
    });
    Add(new Book
    {
      Id = 4,
      Title = "Programming Windows Phone 7"
    });
    Add(new Book
    {
      Id = 5,
      Title = "Visual Studio 2008"
    });
    Add(new Book
    {
      Id = 6,
      Title = "Microsoft .NET 4.0"
    });
    Add(new Book
    {
      Id = 7,
      Title = "ASP.NET 4.0"
    });
  }

  public IEnumerable<book> Get()
  {
    return products;
  }

  public IEnumerable<book> GetList(string search)
  {
    return products.Where(m => m.Title.Contains(search));
  }

  public Book Add(Book item)
  {    
    books.Add(item);
    return item;
  }
}
y finalmente IBookRepository
public interface IBookRepository
{
  IEnumerable<book> Get();
  IEnumerable<book> GetList(string search);
}
Como vemos nada complicado, una simple controladora que accede a un origen de datos un tanto especial, pero con la cual conseguimos simular el acceso a datos, y vemos como podemos consumir estos datos desde cliente.
Nuestro html está formado por los siguiente elementos, un buscador
<h2>Ejemplo de uso de Knockout</h2>
<h3>Listado de libros</h3>
<div id="search">
  <input type="text" data-bind="value: search" />
  <input type="button" data-bind="click: onsearch" value="Search" />
</div>
Un elemento que muestra un mensaje si no hay resultado
<!-- ko if: books().length == 0 -->
  No hay resultados que mostrar
<!-- /ko -->
Una lista para mostrar nuestros resultados
<!-- ko ifnot: books().length == 0 -->
<ul data-bind="foreach: books">
  <li data-bind="text: $data.Title"></li>
</ul>
<!-- /ko -->
Y nuestro modelo
<script type="text/javascript" language="javascript">
  function ViewModel() {
    var self = this;

    self.search = ko.observable('');
    self.books = ko.observableArray([]);

    self.onsearch = function () {
      var search = self.search();
      $.ajax({
        type: 'GET',
        dataType: "json",
        contextType: "application/json; charset=utf-8",
        url: "@Url.Action("book", "api")" + "/get/" + search,
        data: {},
        success: function (pReturn) {
          self.books(pReturn);
        }
      });
    }

    // Búsqueda inicial
    self.onsearch();
  }
  ko.applyBindings(new ViewModel()); 
</script>
En nuestro modelo definimos dos observadores, uno sobre el texto a buscar, y otro sobre la colección de libros que mostraremos. Es importante tener en cuenta que el observador sobre la colección de libro solo hace falta definirlo una vez y no es necesario redefinirlo cada que vez que actualicemos la colección. Es un fallo muy común y que puede darnos algún que otro quebradero de cabeza.

Inicialmente se llama al método onsearch de nuestro modelo, al igual que cada que hagamos click en el botón de buscar. Para la llamada si usaremos jQuery, y simplemente atacaremos nuestra controladora.

Otro detalle, es que para ocultar ya no es necesario poner div innecesarios, ya que he encotnrado una sintaxis más elegante que nos evita crear elementos innecesarios. En vez de poner esto
<div data-bind="if: books().length == 0">
  No hay resultados que mostrar
</div>
ponemos esto
<!-- ko if: books().length == 0 -->
  No hay resultados que mostrar
<!-- /ko -->
Vemos lo simple que queda nuestro código incluso cuando tenemos que cargar dinámicamente datos desde por ejemplo una Api.

En el próximo artículo veremos como podemos crear elementos, en nuestro caso libros, y veremos que lo podemos hacer también de una forma muy simple...

Happy coding!

martes, 22 de mayo de 2012

Introducción a Knockout (IV) - El uso de if, ifnot y visible

Hoy seguiremos con la explicación del funcionamiento básico de knockout. Está vez le tocará el turno al binding visible y veremos como controlar el flujo de la presentación mediante el uso de if o ifnot.

Bien, el propósito del binding visible es mostrar u ocultar un elemento DOM en base a un valor, y como veremos a continuación su funcionamiento es muy simple:
<h3>Listado de libros</h3>
<ul data-bind="foreach: libros, visible: mostrar">
 <li data-bind="text: $data"></li>
</ul>
<input type="checkbox" data-bind="checked: mostrar" /> Mostrar

<script type="text/javascript" language="javascript">
 function ViewModel() {
  var self = this;

  self.libros = ko.observableArray(["Introducing Microsoft Sql Server 2012",
   "Introducing Windows Server 2008 R2",
   "Visual Studio 2010", 
   "Programming Windows Phone7"]);

  self.mostrar = ko.observable(true)
 }
 ko.applyBindings(new ViewModel()); 
</script>
Podemos ver su funcionamiento aquí.

Como vemos, establecemos un checkbox que nos indicará si debemos mostrar la lista de elementos o no. Mediante el dababind checked actualizamos el valor del función observable mostrar, que a su vez, cambiará el estado del listado (ul). Si vemos el código html veremos que se añade display:none al estilo del ul cuando desmarcamos el checkbox.


El funcionamiento de if es similar al del databind visible. La principal diferencia es que mientras visible muestra u oculta en base al css, el databind if elimina los nodos que no serán visible del árbol html.

Modificando un poco nuestro ejemplo anterior ya que no podemos usar el databind forech e if en el mismo elemento veremos como funciona el databind if
<h3>Listado de libros</h3>
<div data-bind="if: mostrar">
 <ul data-bind="foreach: libros">
  <li data-bind="text: $data"></li>
 </ul>
</div>
<input type="checkbox" data-bind="checked: mostrar" /> Mostrar

<script type="text/javascript" language="javascript">
 function ViewModel() {
  var self = this;

  self.libros = ko.observableArray(["Introducing Microsoft Sql Server 2012",
   "Introducing Windows Server 2008 R2",
   "Visual Studio 2010", 
   "Programming Windows Phone7"]);

  self.mostrar = ko.observable(true)
 }
 ko.applyBindings(new ViewModel()); 
</script>
Podemos ver el ejemplo anterior funcionando aquí

Si vemos el código html del ejemplo anterior veremos esto cuando el checkbox está desmarcado


Como vemos es muy fácil modificar el aspecto de nuestra presentación usando los databind visible e if dotando a nuestra interfaz de una mayor dinamismo.

Happy coding!

lunes, 14 de mayo de 2012

Introducción a Knockout (III) - El uso del data-bind options

A raíz de una pregunta sobre como podríamos detectar la selección de un elemento en un combobox o dropdown me he puesto a mirar a fondo como funciona el data-bind options de knockout. Mirando su documentación, este data-bind, se usa en los dropdown-list o en los multiselect-list. Fuera de estos elementos el elementos este data-bind no realiza ninguna acción.

Para usarlo tan sólo debemos hacer poner esto en nuestro html
<h2>Ejemplo de uso de Knockout</h2>
<h3>Seleccione una opción</h3>
<select data-bind="options: optionValues, value: selectedOptionValue"></select>
y definir nuestro modelo MVVM de la siguiente manera
<script type="text/javascript" language="javascript">
    function ViewModel() {
     var self = this;

     self.optionValues = ['Alpha', 'Beta', 'Gamma'];
     self.selectedOptionValue = ko.observable("Gamma");
     });
    }
    ko.applyBindings(new ViewModel());
</script>
Como vemos, usamos un select html donde definiremos el data-bind options, asignándole los datos que tengamos en la propiedad optionValues de nuestro modelo y asignando el observador selectionOptionValue para detectar los cambios. En caso que queramos detectar el cambio de valores, podemos usar la función suscribe de la siguiente manera
<script type="text/javascript" language="javascript">
    function ViewModel() {
     var self = this;

     self.optionValues = ['Alpha', 'Beta', 'Gamma'];
     self.selectedOptionValue = ko.observable("Gamma");
     self.selectedOptionValue.subscribe(function (value) {
      alert("El valor seleccionado es " + value);
     });
    }
    ko.applyBindings(new ViewModel());
</script>
Podemos complicar un poco nuestro modelo para asignar un objeto en vez de una lista plana de elementos. Esto se acerca más a la realidad donde probablemente mostremos algún objeto del tipo clave / valor. También podemos hacer que el dropdown-list presente el clásico mensaje "Seleccione un valor...". Para esto definiremos nuestro select html de la siguiente manera
<h2>Ejemplo de uso de Knockout</h2>
<h3>Seleccione una opción</h3>
<select data-bind="options: optionValues, optionsText: 'value', optionsValue: 'id', value: selectedOptionValue, optionsCaption: 'Seleccione una opción...'"></select>
y nuestro modelo así
<script type="text/javascript" language="javascript">
    function ViewModel() {
     var self = this;

     self.optionValues = [{ id: "1", value: "valor1" }, { id: "2", value: "valor2"}];
     self.selectedOptionValue = ko.observable("2");
     self.selectedOptionValue.subscribe(function (value) {
      alert("El valor seleccionado es " + value.id);
     });
    }
    ko.applyBindings(new ViewModel());
</script>
Como vemos definimos un objeto simple con una propiedad id y otra propiedad valor, que asignamos a la propiedad optionValues que será usada para rellenar el dropdown-list. También definimos el optionsText, que indica la propiedad usada como texto en el select, y el optionsValue que no indica el value. El mensaje "Seleccione una opción..." es definido mediante la opción optionsCaption. Vemos que al igual que en las lista planas también podemos hacer uso de la función suscribe para manejar nosotros la selección de un elemento.

Vemos como con knockout es bastante fácil manejar los dropdown-list.

Happy coding!

domingo, 13 de mayo de 2012

jQuery desactivar el menú contextual del navegador

Para desactivar el menú contextual (típicamente el menú que sale a hacer click con el botón derecho sobre la página cargada) tan solo deberemos añadir el siguiente código a nuestra página
$(document).ready(function(){
    $(document).bind("contextmenu", function(e){
        return false;
    });
});
Si lo quisiéramos poner en todas las páginas de nuestra aplicación web tan solo debemos poner el código anterior en nuestra MasterPage (ASP.NET) o Layout Page (ASP.NET MVC).

Happy coding!

viernes, 11 de mayo de 2012

ASP.NET MVC4 Validando un campo que depende de otro

Parece que estoy destinado a tropezarme con algunos problemas cada vez que quiero validar algo en las clases modelo de mis vistas. Lla parte positiva es que uno sigue aprendiendo, así que, vamos al lio. Como siempre, supongamos esta vista y este modelo simplificados

Vista
<h2>Index</h2>
@using (Html.BeginForm())
{
 <div>Otros @Html.CheckBoxFor(m => m.Otros)</div>
 <div>Comentario @Html.TextBoxFor(m => m.Comentario)</div>
 <div>@Html.ValidationMessageFor(m => m.Comentario)</div>
 <input type="submit" value="Enviar" />
}
Modelo
public class IndexModel
{
 public bool Otros { get; set; }
 public string Comentario { get; set; }
}
Como siempre, nada raro. Se trata de validar el campo Comentario siempre y cuando el valor de campo Otros sea verdadero. Una vez más busqué algo que de serie me hiciera esta validación pero no encontré nada, así que una vez más me tocó hacer mi propio validador. Para crear un validador simplemente tenemos que heredar de la clase ValidationAttribute y sobreescribir el método IsValid. Pero tendremos que sobreescribir el método IsValid que tiene dos argumentos (object value y ValidationContext validationContext) ya que tenemos que acceder al contexto de validación para obtener el valor de una propiedad. El esqueleto básico del validador sería algo así
public class RequiredIfTrue : ValidationAttribute
{
  protected override ValidationResult IsValid(object value, ValidationContext validationContext)
  {
    return base.IsValid(value, validationContext);
  }
}
Bien, lo primero que haremos será definir una propiedad (TrueOrFalsePropertyName) en nuestro validador para indicar el nombre de la propiedad enlazada. En nuestro caso, es la propiedad que deberá tener el valor "verdadero" para seguir con la validación, o sea, la propiedad Otros. Luego, en el método IsValid, accederemos a esa propiedad, obtendremos su valor, y comprobaremos si tenemos o no que continuar con la validación. También haremos algunas validaciones como que el nombre de la propiedad es válido y que el tipo que devuelve es un boolean En estos casos, he optado por lanzar una excepción para detener el flujo del programa ya que considero que son fallos de "programación" y que estos fallos no llegarían al usuario final. Con estás premisas nuestro validador queda de la siguiente forma
public class RequiredIfTrue : ValidationAttribute
{
 public string TrueOrFalsePropertyName { get; set; }

 protected override ValidationResult IsValid(object value, ValidationContext validationContext)
 {
  if (string.IsNullOrEmpty(TrueOrFalsePropertyName))
   throw new Exception("No se ha especificado la propiedad 'TrueOrFalsePropertyName'");

  // Obtenemos la propiedad
  PropertyInfo property = validationContext.ObjectType.GetProperty(TrueOrFalsePropertyName);
  if (property == null) 
   throw new Exception(string.Format("No se encuentra la propiedad '{0}'", TrueOrFalsePropertyName));
  
  // Comprobamos que la propiedad sea de tipo System.Boolean
  if (property.PropertyType != typeof(System.Boolean))
   throw new Exception(string.Format("La propiedad '{0}' no es de tipo System.Boolean", TrueOrFalsePropertyName));

  bool pValue = (bool)property.GetValue(validationContext.ObjectInstance, null);

  // Comprobamos el valor
  if (pValue && (value == null || (string)value == string.Empty)) return new ValidationResult(ErrorMessage);
  
  return null;
 }
}
Y el funcionamiento del validador se puede ver en las siguientes imágenes.



Si no marcamos el campo otros da igual el valor


Si marcamos el campo Otros, el campo Comentario es obligatorio.


Todo correcto otra vez

Happy coding!

Nota: Y para estrenar mi cuenta en SkyDrive, se pueden descargar el código fuente de los siguientes enlaces.
MvcApplication1.rar
MvcApplication1.zip

jueves, 10 de mayo de 2012

Windows Phone - Como añadir más controles a nuestros proyectos

Una vez superado el clásico Hello World! y tras hacer las primeras pruebas con Windows Phone me he dado cuenta que la cantidad de controles que tenemos a nuestra disposición en el Visual Studio es relativamente limitada sobre todo si estamos acostumbrados a la cantidad de controles que tenemos en el desarrollo de aplicaciones de escritorio.


Mirando un poco de donde podemos obtener algunos controles llegué a un proyecto en codeplex llamado Silverlight for Windows Phone Toolkit. Nunca me ha gusta usar controles de terceros pero en este caso la librería está mantenida por gente de Microsoft así que haré una excepción con ellos. Indagando un poco más llegué a que esta librería de controles puede ser descargada por dos vías.
Ambas alternativas son igual de validas, así que me centraré en la instalación desde nuget. Para ello como siempre, accedemos a la Package Manager Console y escribimos lo siguiente
PM> Install-Package SilverlightToolkitWP
Y si todo va bien veremos lo siguiente
Successfully installed 'SilverlightToolkitWP 4.2011.12.14'.
Successfully added 'SilverlightToolkitWP 4.2011.12.14' to PhoneApp.

PM>
Tras esto esto, tendremos que añadir estos controles a nuestro Visual Studio, así que vamos a la barra izquierda (donde salen por defecto lo controles) y le damos al botón derecho y hacemos click en Add Tab para añadir una pestaña donde pondremos nuestros controles. A la nueva pestaña le podemos poner el nombre de Windows Phone Toolkit


Después haremos click con el botón derecho debajo de la nueva pestaña y le daremos a Choose Items...


En la ventana que se nos abre seleccionamos la pestaña Windows Phone Components y luego le damos al botón de Browse

Tras esto se nos abrirá el clásico File Dialog de Windows onde buscaremos la librería Microsoft.Phone.Control.Toolkit.dll. La seleccionaremos y le daremos al botón de Abrir.


Una pequeña aclaración. Si hemos optado por descargar el ensamblado vía nuget, éste se encontrará en la carpeta packages de nuestra solución. Hemos de tener en cuenta que estamos añadiendo un paquete de controles al Visual Studio por lo que si creamos otra aplicación Windows Phone esta librería de controles se irá a buscar a esa carpeta (la del proyecto con el que estemos trabajando en ese momento). Por lo que si borramos el proyecto nos quedamos sin controles. En este tipo de situaciones quizá es mejor instalar el ejecutable de la página de codeplex. En ese caso la ruta donde se encuentra la dll es C:\Program Files (x86)\Microsoft SDKs\Windows Phone\v7.1\Toolkit\Oct11\Bin

Bien, tras seleccionar el paquete veremos toda la lista de controles disponibles y tendremos marcados los que se añadirán.


Le damos a OK y ya tendremos todo un nuevo mundo de controles a nuestro alcance.


Para la próxima espero poder escribir sobre el acceso a datos desde Windows Phone, que como siempre me ha sorprendido por su sencillez.

Happy coding!

martes, 8 de mayo de 2012

Introducción a Knockout (II) - El uso de for-each y click

En mi anterior artículo hice una pequeña introducción a knockout. Hoy vamos a ver como nos puede ayudar a crear elementos en nuestra interfaz y a su vez interactuar con estos elementos de una manera muy fácil y "elegante".

Para esto tan solo debemos usar el data-bind foreach dado por knockout.Veamos su funcionamiento con el siguiente ejemplo:
<h3>Listado de libros</h3>

<ul data-bind="foreach: libros">
  <li data-bind="text: $data"></li>
</ul>

<script type="text/javascript" language="javascript">
  function ViewModel() {
    var self = this;
  
    self.libros = ko.observableArray([]);

    // Creamos nuestra colección de libros
    var t = ["Introducing Microsoft Sql Server 2012",
      "Introducing Windows Server 2008 R2",
      "Visual Studio 2010", 
      "Programming Windows Phone7"];
    self.libros(t);
  }
  ko.applyBindings(new ViewModel()); 
</script>
Vemos que creamos nuestro <ul> y ahí establecemos el data-bind foreach, sobre una propiedad de nuestro modelo. Esta propiedad está definida como un array sobre el que establecemos un observador para que cada vez que cambie se rehaga nuestro listado. Para ver este comportamiento vamos a utilizar otro data-bind, en este caso usarmos el data-bind click que nos permite interactuar sobre un click en un elemento determinado.
<h3>Listado de libros</h3>
<ul data-bind="foreach: libros">
  <li data-bind="text: $data"></li>
</ul>
<span data-bind="click: recargar" style="cursor: pointer">Recargar lista</span>

<script type="text/javascript" language="javascript">
  function ViewModel() {
    var self = this;
  
    self.libros = ko.observableArray([]);

    // Creamos nuestra colección de libros
    var t = ["Introducing Microsoft Sql Server 2012",
      "Introducing Windows Server 2008 R2",
      "Visual Studio 2010", 
      "Programming Windows Phone7"];
    self.libros(t);

    self.recargar = function () {
      t = ["Visual Studio 2008",
        "Microsoft .NET 4.0",
        "ASP.NET 4.0"];

      self.libros(t);
    }
  }
  ko.applyBindings(new ViewModel());
</script>
En este ejemplo vemos como inicialmente cargamos la lista con 4 títulos y tras hacer click sobre el recargar, ésta se recarga con los tres nuevo libros definidos en el método recargar. Vemos que el código es muy limpio sobre todo en la parte html, y con poco esfuerzo podemos cambiar la función recargar de estática a dinámica haciendo una llamada ajax para obtener datos de nuestro servicio. Puedes probar el ejemplo haciendo click aquí.

Antes la sencillez del código, la pregunta sería, ¿cómo podríamos hacer esto con jQuery? Pues sin ser un experto en jQuery sería algo así
<h3>Listado de libros</h3>
<ul id="libros"></ul>
<span id="recargar" style="cursor: pointer">Recargar lista</span>​
$(document).ready(function() {
    var t = ["Introducing Microsoft Sql Server 2012",
      "Introducing Windows Server 2008 R2",
      "Visual Studio 2010", 
      "Programming Windows Phone7"];
    bind(t);    
    
    $('#recargar').click(function() {
        t = ["Visual Studio 2008",
          "Microsoft .NET 4.0",
          "ASP.NET 4.0"];
        bind(t);
    });
    
    function bind(t)
    {
        $('#libros').empty();
        $.each(t, function(index, item) {
            $('#libros').append('<li>'+item+'</li>');
        });
    }
});​
Puedes probar el ejemplo aquí

Happy codding!

lunes, 7 de mayo de 2012

Introducción a Knockout (I)

Desde hace tiempo cada vez programo más en cliente. Para esto jQuery nos ofrece una gran ayuda, sobre todo con su gran cantidad de selectores que nos permite realizar tareas comunes como mostrar u ocultar información o crear elementos html "al vuelo" de una manera fácil.

Pues bien, una de las cosas que más odio de jQuery es tener que recorrer un div (o elemento html al gusto) para extraer toda la información que tienen los inputs para montar un JSON, y éste enviarlo al servidor. De igual forma es tedioso tener que mapear a mano cada valor recibido de "nuestro servidor" en los diferentes elementos html que tengamos en nuestra página. Tengo hecho un pequeño framework que me ayuda en este tipo de tareas (marcando los inputs, checkboxs o radiobuttons en los que deseo insertar o extraer la información) pero nunca me ha terminado de convencer.

Para resolver todo esto he descubierto, gracias a la plantilla de ASP.NET MVC4, un script que se llama Knockout. Knockout nos ayuda a interactuar de una manera fácil con la interfaz mediante el patrón MVVM (poniendo cada cosa en su sitio).

Y como todo se entiende mejor con código vamos a poner un ejemplo.
Nombre: <input data-bind="value: Nombre" type="text" />
Primer apellido <input data-bind="value: PrimerApellido" type="text" />
function ViewModel() {
    var self = this;
    
    self.Nombre = "John";
    self.PrimerApellido= "Doe";
}

// Activates knockout.js
ko.applyBindings(new ViewModel());​
Bien, ¿que hemos hecho en el ejemplo? Pues hemos vinculado (bindeado en ese idioma que tenemos los informáticos) a través del atributo data-bind la propiedad value del input (o sea, lo que se muestra) con el valor de Nombre o PrimerApellido que se definan en nuestro modelo (ViewModel).

Pero no solo estamos atados a datos estáticos sino que podemos quedarnos a la espera a que ese datos cambien para actualizar el valor en el otro lado. Veamos el siguiente ejemplo
Nombre: <input type="text" data-bind="value: Nombre" /><br />
Primer apellido <input type="text" data-bind="value: PrimerApellido" /><br />
Nombre: <span data-bind="text: Nombre"></span>
Primer apellido: <span data-bind="text: PrimerApellido"></span ><br />
function ViewModel() {
    var self = this;
    
    self.Nombre= ko.observable("John");
    self.PrimerApellido= ko.observable("Doe");
}

// Activates knockout.js
ko.applyBindings(new ViewModel());​
Vemos que si cambiamos el valor de uno de los input, éste al perder el foco actualiza el valor del span, ya que ambos se han enlazado (incluido los cambios) al valor que tenga el modelo. Para esto hemos definido un observador que nos da knockout. Como vemos con poco código hemos vinculado la actualización de un campo con nuestro modelo (y viceversa). Lo cual es ideal, para el caso de recibir valores de nuestra API a través de llamadas ajax.

A veces nos puede venir bien combinar dos valores, y establecerlos en una propiedad, pero a su vez estar atentos a su cambios. Este ejemplo lo vemos con el siguiente código
Nombre: <input type="text" data-bind="value: Nombre" /><br />
Primer apellido <input type="text" data-bind="value: PrimerApellido" /><br />
Hola <span data-bind="text: Mensaje"></span ><br />
function ViewModel() {
    var self = this;
    
    self.Nombre= ko.observable("John");
    self.PrimerApellido= ko.observable("Doe");
    self.Mensaje= ko.computed(function() {
        return this.Nombre() + " " + this.PrimerApellido();    
    }, this); 
}

// Activates knockout.js
ko.applyBindings(new ViewModel());​
Como vemos el uso de un "modelo" con estos observadores hacen que mejore la legibilidad del código que escribimos, aunque la potencia de Knockout no queda aquí, ya que también nos permite:
  • Engancharnos a eventos como el click o el checked
  • Establecer observadores sobre colecciones (útil para mantener una lista de los checks marcados para un filtrado).
  • Mostrar u ocultar información en base a criterios (por ejemplo una lista vacia) que podemos definir en nuestro modelo.
  • Iterar sobre colecciones para crear elementos en la UI.

domingo, 6 de mayo de 2012

jQuery maxlength en un textbox multilinea (textarea)

Si alguna vez hemos usado en asp.net los textbox multilinea nos habremos dado cuenta que la propiedad maxlength no funciona.
<asp:TextBox ID="TextBoxMultiLine" MaxLength="1" TextMode="MultiLine" runat="server"></asp:TextBox>
Que en html quedaría
<textarea name="ctl00$MainContent$TextBoxMultiLine" rows="2" cols="20" id="MainContent_TextBoxMultiLine"></textarea>
Para hacer que funcione podemos usar el siguiente código en jQuery
$(document).ready(function () {
  $('[id$=_TextBoxMultiLine]').keypress(function (e) {
    if ($(this).val().length + 1 > 1)
      e.preventDefault();
  });
});
El motivo de usar en el selector de jQuery el identificador _TextBoxMultiLine, es por la forma que tiene ASP.NET de asegurarse que un identificador sea único en la página. Si usamos ASP.NET 4.0 podemos hacer uso del modificador ClientIDMode y asignarlo con Static para evitar que el ID cambie. En se caso el script quedaría de la siguiente
<asp:TextBox ID="TextBoxMultiLine" MaxLength="1" TextMode="MultiLine" ClientIDMode="Static" runat="server"></asp:TextBox>
$(document).ready(function () {
  $('#TextBoxMultiLine').keypress(function (e) {
    if ($(this).val().length + 1 > 1)
      e.preventDefault();
  });
});
Otra posibilidad es no poner usar el atributo ClientIDMode y usar el ClientID de la siguiente manera
$(document).ready(function () {
  $('#<%= TextBoxMultiLine.ClientID %>').keypress(function (e) {
    if ($(this).val().length + 1 > 1)
      e.preventDefault();
  });
});
Si no queremos usar jQuery, podríamos llamar al un método en el evento keyUp. Para eso añadimos esto en el Page_Load de nuestra página
TextBoxMultiLine.Attributes.Add("onKeyUp", "Count(this, 1)");
function Count(text, long) {
  var maxlength = new Number(long);
  if (text.value.length > maxlength) 
    text.value = text.value.substring(0, maxlength);  
}

Happy coding!

sábado, 5 de mayo de 2012

jQuery input text numérico

Aquí les dejo un pequeño trozo de código para hacer que un input de tipo text solo admita caracteres numéricos ([0-9])
$('input').keydown(function(e) {  
  // Admite [0-9], BACKSPACE y TAB
  if ((e.keyCode < 48 || e.keyCode > 57) && (e.keyCode < 96 || e.keyCode > 105) && e.keyCode != 8 && e.keyCode != 9)
      e.preventDefault();
});
Pueden probar el código aquí ( http://jsfiddle.net/z2aEL/ )

Happy coding!

viernes, 4 de mayo de 2012

ASP.NET MVC4 Validando que un campo sea true o checked

Parece que todo lo que me propongo hacer últimamente se convierte en una odisea, por muy fácil que pueda parecer al principio. Tras mi anterior aventura para validar que dos campos sean iguales en un formulario de registro esta vez le ha tocado el turno a la validación de un valor en un checkbox. Supongamos una vez más un modelo bastante simple

Modelo
public class IndexModel
{
  public bool AceptoCondiciones { get; set; }
}
Vista
@using (Html.BeginForm())
{   
  <div>@Html.CheckBoxFor(m => m.AceptoCondiciones) Acepto las condiciones de uso</div> 
  <div>@Html.ValidationMessageFor(m => m.AceptoCondiciones)</div>
 
  <input type="submit" value="Enviar" />
}
La idea es comprobar que el usuario acepta las condiciones de uso. Algo bastante típico en formularios de registro. La primera idea fue marcar la propiedad AceptoCondiciones con el DataAnnotation [Required], pero tras escribirlo (no hizo falta ni probarlo) me di cuenta que no iba a funcionar, ya que el checkbox siempre tiene un valor, o verdadero o falso (marcado o desmarcado) por lo que la regla Required no nos valdría.

Tras mirar y mirar por google y darme cuenta que ya llevaba bastante tiempo perdido no me quedo más remedio que lanzarme a hacer mi propio validador. Para hacer una validador propio tan solo debemos heredar de la clase ValidationAttribute y sobreescribir el método IsValid. Pensando en el futuro añadí una propiedad ValidValue que nos indica cual es el valor que damos por válido, no sea que alguna vez necesite tener desmarcado un check. Con esto el validador quedaría así
public class IsTrueOrFalseAttribute : ValidationAttribute
{
  /// 
  /// Valor permitido
  /// 
  public bool ValidValue { get; set; }

  public override bool IsValid(object value)
  {
    if (value == null || value.GetType() != typeof(bool)) return false;

    return ((bool)value == ValidValue ? true : false);
  }
}
Y añadimos el atributo al modelo
public class IndexModel
{
  [IsTrueOrFalse(ValidValue=true)]
  public bool AceptoCondiciones { get; set; }
}
Con esto esto ya podemos probar nuestro validador y ver que funciona de la manera esperada


Pero, siempre hay un pero, este validador presenta un problema y es que siempre irá al servidor para realizar la validación. En mi caso no hay problema porque en el formulario de registro siempre voy al servidor para comprobar algunas cosas, como el nombre de usuario, pero en otros escenarios puede ser que no tengamos que ir al servidor para nada, e ir para solo comprobar si un checkbox está marcado es innecesario.


Como me gusta investigar y aprender cosas nuevas, decidí hacer que mi validador también tuviera soporte para cliente. La verdad es que esta parte está bastante mal documentada pero básicamente lo primero que tenemos que hacer es que nuestro validador implemente la interfaz IClientValidatable. En nuestro caso el validador quedaría de la siguiente manera
public class IsTrueOrFalseAttribute : ValidationAttribute, IClientValidatable
{
  /// 
  /// Valor permitido
  /// 
  public bool ValidValue { get; set; }

  public override bool IsValid(object value)
  {
    if (value == null || value.GetType() != typeof(bool)) return false;

    return ((bool)value == ValidValue ? true : false);
  }

  IEnumerable<modelclientvalidationrule> IClientValidatable.GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
  {
    var rule = new ModelClientValidationRule
    {
        ErrorMessage = String.IsNullOrEmpty(ErrorMessage) ? FormatErrorMessage(metadata.PropertyName) : ErrorMessage,
        ValidationType = "istrueorfalse"
    };
    // Transformamos el valor boolean a un string en minúsculas para que no falle la transformación en cliente de string a boolean.
    rule.ValidationParameters["validvalue"] = (this.ValidValue ? "true" : "false");

    yield return rule;
  }
}
Y luego en un fichero .js debemos añadir el siguiente código
$.validator.addMethod("istrueorfalse", function (value, element, param) {
  // Comparamos el valor checked del elemento con el valor permitido pasado por parámetro
  return element.checked == Boolean(param.validvalue);
});

$.validator.unobtrusive.adapters.add('istrueorfalse', ['validvalue'], function (options) {
  options.rules['istrueorfalse'] = options.params;
  options.messages['istrueorfalse'] = options.message;
});
Si ejecutamos nuestro código veremos que realiza la validación sin la necesidad de ir a cliente.

Nota: Para activar las validaciones en cliente (pese a que en ASP.NET MVC4 ya viene por defecto) debemos asegurarnos que tenemos estas lineas en el web.config
<appSettings>
  <add key="ClientValidationEnabled" value="true" />
  <add key="UnobtrusiveJavaScriptEnabled" value="true" />
</appSettings>
También hay que comprobar que tenemos añadidas las referencias a los scripts jquery-1.6.2.js, jquery.validate.js, jquery.validate.unobtrusive.js y al fichero que creemos para añadir las lineas donde añadimos el validador y el adaptador.

Como vemos tampoco es extraordinariamente complicado validar nuestros datos en cliente así que, lo mejor que podemos hacer es hacerlo...

Happy coding!

miércoles, 2 de mayo de 2012

ASP.NET MVC4 Validando que dos campos sean iguales

Algo muy típico en el registro de usuarios es tener que introducir la contraseña dos veces para asegurarnos que la contraseña introducida es la misma. Lo normal sería poner esta validación como un DataAnnotation en la clase modelo con la que trabaje nuestra vista. Lo que vendría a ser algo así

Modelo
using System.ComponentModel.DataAnnotations;

public class IndexModel
{
  [Required(ErrorMessage = "La constraseña es obligatoria")]
  public string Password { get; set; }
  public string ConfirmPassword { get; set; }
}
Vista
@using (Html.BeginForm())
{
  <div class="editor-label">
    <label>Password</label>
    @Html.TextBoxFor(m => m.Password)
    @Html.ValidationMessage("Password")
  </div>
  <div class="editor-label">
    <label>Confirm password</label>
    @Html.TextBoxFor(m => m.ConfirmPassword)
    @Html.ValidationMessage("ConfirmPassword")
  </div>
  <input type="submit" value="Submit" />
}
Como vemos es un modelo muy simple en el que la contraseña es obligatoria y en el que tenemos que validar que ambas contraseñas introducidas sean iguales. Pues bien, lo primero que hice fue mirar en los DataAnnotations si había algún modificador para lo que yo estaba buscando, y cual fue mi sorpresa al ver que no había nada.

Lo siguiente que se me ocurrió fue hacer un validador personalizado, pero tras mirar como se hace me pareció que esto era demasiado complicado para lo que yo quería hacer y que seguro que algo tenía que haber en el framework para resolverlo.

Tras mucho indagar logré encontrarlo y en atributo en concreto se llama Compare (que original), pero en vez de estar definiddo en el namespace System.ComponentModel.DataAnnotations está definido en el namespace System.Web.Mvc. Así que nuevo modelo quedaría definido de la siguiente manera
using System.ComponentModel.DataAnnotations;
using System.Web.Mvc;

public class IndexModel
{
  [Required(ErrorMessage = "La constraseña es obligatoria")]
  [Compare("ConfirmPassword", ErrorMessage = "Las constraseñas no son iguales")]
  public string Password { get; set; }
  public string ConfirmPassword { get; set; }
}
Mucho más simple que hacer nosotros el validador.
Happy coding!