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!