Vous êtes sur la page 1sur 94

Knockout.

js
Utilice Knockout.js para diseñar y crear aplicaciones web dinámicas
del lado del cliente que son extremadamente sensibles y fáciles de " Este libro es un gran
mantener. Este ejemplo Libro muestra cómo usar este marco ligero
de JavaScript y su Model-View-ViewModel (MVVM) patrón. recurso para cualquier desarrollador
Aprenderá cómo construir su propio Datos, extender el marco con buscando hacer su
funciones reutilizables y trabajar con Un servidor para mejorar su
aplicación de cliente con persistencia. En la final Capítulo, usted usuario de la aplicación web
construirá un carro de la compra para ver cómo todo cabe juntos. experiencia pop
Si usted es un desarrollador web con experiencia en JavaScript, presenta las características
HTML y CSS, Usted está listo para Knockout. y beneficios de
■ Aprenda a crear un modelo de vista Knockout.js con
■ Enlazar HTML de datos y atributos y clases CSS y estilos ejemplos extensos y
■ Entender vinculantes en la jerarquía contexto del explicaciones en profundidad.
Knockouts de gracia de datos
■ Use las propiedades que cambian dinámicamente a través usted también se beneficia
de usuario Interacción del autor
■ Trabajar con las formas mediante el uso de varias recomendaciones y
ediciones diferentes
■ enlazar varios ViewModels en una sola página consejos procedentes de
■ Extender o adjuntar funciones personalizadas a los lecciones aprendidas
observables su experiencia en la construcción
■ Realizar las interacciones del lado del servidor con jQuery
■ Asignar un objeto JavaScript o aplicar los datos JSON a un aplicaciones web. “
-Steven Kennedy
nuevo objeto
Desarrollador Senior de Software en Fusebill

Jamie Munro ha estado desarrollando sitios web y aplicaciones


web por más de 15 años. Durante los últimos seis años, ha sido
mentor de jóvenes desarrolladores para Mejorar sus habilidades
de desarrollo web. Jamie sitio web (http: //www.webistrate.com).
ofrece ejemplos para ayudar a los desarrolladores web a ampliar
su experiencia.
Knockout.js
Creación de aplicaciones web dinámicas del lado del cliente

Jamie Munro
Knockout.js
De Jamie Munro

Copyright © 2015 Jamie Munro. Todos los derechos reservados.


Impreso en los Estados Unidos de América.

Publicado por O'Reilly Media, Inc., 1005 Gravenstein Highway North, Sebastopol, CA 95472.

Los libros de O'Reilly se pueden comprar para uso educativo, comercial o de promoción de ventas. Las
ediciones en línea son También disponible para la mayoría de los títulos ( http://safaribooksonline.com ). Para
obtener más información, póngase en contacto con nuestra empresa / departamento de ventas institucionales:
800-998-9938 o corporate@oreilly.com.

Editor: Meg Foley Indexador: Judy McConville


Editor de Producción: Kara Ebrahim Diseñador de interiores: David Futato
Corrector de estilo: Gillian McGarvey Cover Designer: Ellie Volckhausen
Corrector de pruebas: Marta Justak Illustrator: Rebecca Demarest

Diciembre 2014: Primera edición

Historial de revisiones para la primera edición


2014-12-08: Primera versión

Ver http://oreilly.com/catalog/errata.csp?isbn=9781491914311 para los detalles de la versión.

El logotipo de O'Reilly es una marca registrada de la imagen de portada de un negro O'Reilly Media,
Inc. Knockout.js,Canguro de árbol, y el vestido comercial relacionado son marcas registradas de O'Reilly
Media, Inc.

Aunque el editor y el autor han utilizado esfuerzos de buena fe para garantizar que la Instrucciones contenidas
en este trabajo son exactas, el editor y el autor renuncian a toda responsabilidad por errores u omisiones,
incluyendo, sin limitación, responsabilidad por los daños resultantes del uso de O la confianza en este
trabajo. El uso de la información e instrucciones contenidas en este trabajo está en su propio riesgo. Si
cualquier muestra de código u otra tecnología que este trabajo contiene o describe está sujeto a código abierto
Licencias o los derechos de propiedad intelectual de terceros, es su responsabilidad asegurarse de que su uso
Cumple con dichas licencias y / o derechos.
Este libro está dedicado a mi familia. Espero que aprendas, como yo, que cuando
Trabajar duro y poner su mente a algo, usted puede lograr cualquier cosa!
Tabla de contenido

Prefacio. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Vii

1. Introducción a KnockoutJS. . . . . . . . . ……………. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1


Sintaxis de vinculación de datos 1
Ejemplo de encuadernación de datos 2
¿Qué es MVVM? 3
Creación de un ViewModel 3
Modelos de vista orientados a objetos 4
ViewModels con parámetros 5

2. El entradas y salidas de enlace de datos. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7


Datos HTML vinculantes 7
Atributos HTML de enlace, clases CSS y 8
estilos CSS
Encuadernación de datos basada en la 9
condición

3. Comprender el Contexto enlace de datos. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. . . . . . . . . 11


Foreach encuadernación 12
Foreach Callbacks (Eventos) 15
Con encuadernación 17

4. dinámicamente cambiar las propiedades. . . . . . . . . .. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21


Definición de un observable 21
PuraComputado Observables 23
Mostrando y ocultando elementos 24
Adición y eliminación de elementos 26
Cuándo utilizar Observables 28
5. Trabajar con formularios. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
Fijaciones de Datos de Formulario 31
Fijaciones de datos de eventos 33
Escuchando los cambios 34
Validación de formularios discreta 36

6. Múltiples ViewModels, Fijaciones de datos personalizados y plantillas. . . . . . . . . . . . . . . . . . . 39


Vinculación de varios modelos de vista 39
Vinculación a un editor WYSIWYG 40
Vinculación a una plantilla eliminatoria 43

7. observables potenciador. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
Extendiendo observables 47
Adición de funciones personalizadas a 49
observables
Observaciones de limitación de la tasa 51

8. Interacción del lado del servidor. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55


Envío de datos 55
Recepción de datos 58

9. El Mapping Plugin. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63
Mapa desde un objeto 63
Mapa de JSON (o un servidor) 65
Observando e ignorando propiedades 68
específicas

10. Un ejemplo práctico. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71


Construcción de un carrito de compras 71

11. Acontinuación pasos. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79


La sencillez de Knockout
79

El contexto del usuario


80

Actualizaciones de página para actualizaciones de contenido


80
Conclusión 81
12. Index . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
Prefacio

Aplicaciones dinámicas del lado del cliente


Interfaces de usuario (UI) son cada vez más populares, al igual que la web sensible diseño. Sitios
web extremadamente populares como Gmail, Facebook y Twitter han adoptado Aplicaciones
cliente-lado extremadamente responsivas.

Rara vez ves una actualización de página completa; En su lugar, el contenido se carga en
línea. Más importante, su contexto se mantiene y no se ven obligados a saltar entre varias páginas.

Un ejemplo perfecto de esto es Gmail. Irónicamente, vino de escribir este libro. yo era Revisar un
correo electrónico de mi editor y revisor técnico. Tengo el correo electrónico abierto Y quería
responder a ella. Debido a que Gmail colocó mi respuesta en línea, podría fácilmente Revisar el
correo electrónico y escribir mi respuesta al mismo tiempo, sin necesidad de saltar
O tienen dos ventanas abiertas.

KnockoutJS hace que la implementación de aplicaciones web como ésta sea rápida, eficiente y
Importante, fácil de mantener!

Instalación de KnockoutJS
KnockoutJS hizo su debut en julio de 2010. Fue lanzado como un código abierto Proyecto del autor
Steve Sanderson. Ahora está siendo mantenido por el código abierto comunidad. La versión 3.0 fue
lanzada a finales de 2013.

Sigue evolucionando diariamente con nuevas características, mejoras y correcciones de


errores. Todo Los ejemplos de este libro utilizan la versión 3.2 (la versión estable).

La configuración de KnockoutJS es muy simple. Comience visitando las descargas KnockoutJS la


página .

Puede descargar la versión minificada de Knockout haciendo clic derecho (Ctrl + Click para Mac)
y seleccionando la opción "Guardar enlace como ...".

Vii
Sugiero crear una nueva carpeta para todos los ejemplos de código de este libro. Dentro de eso
carpeta, crea una subcarpeta llamada js y coloque el archivo nocaut-3.2.0.js interior.

¿Ha cambiado la versión KnockoutJS?


Es bastante normal que los frameworks de código abierto populares evolucionen bastante
rápido. Si la versión actual de Knockout no es 3.2 cuando Descargarlo, los ejemplos de este
libro deben continuar trabajo. Sin embargo, deberá actualizar lo siguiente:
<script type='text/javascript' src='js/knockout-
3.2.0.js'></script>
A:

<script type='text/javascript'
src='js/knockout-<current-version>.js'></script>

Usted tendrá que reemplazar <actual-version> con el valor correcto.

Si desea utilizar la versión 3.2 como los ejemplos de este libro, puede visitar
el proyecto KnockoutJS en Github y apoderarse de su versión de elección.

Una vez descargado el framework Knockout, cree una página HTML que cargue el Marco, como
en Ejemplo P-1 .

Ejemplo P - 1. Instalación de KnockoutJS

<!DOCTYPE html>
<html>
<head>
<title>Installing KnockoutJS</title>
</head>
<body>
<script type='text/javascript' src='js/knockout-3.2.0.js'></script>
</body>
</html>

En el ejemplo anterior, incluí KnockoutJS justo antes de la etiqueta de body final. Esto es hecho
para permitir que el navegador web procese el HTML sin esperar el archivo JavaScript a descargar
y ejecutar.

Algunos desarrolladores prefieren colocar la inclusión de JavaScript dentro de la etiqueta del


head. Esto es completamente válido, especialmente en situaciones en las que no desea que el
usuario interactúe con su sitio web antes de que todos los archivos JavaScript sean cargados y
procesados por el navegador.

Viii | Prefacio
Convenciones utilizadas en este libro

Las siguientes convenciones tipográficas se utilizan en este libro:

Itálico
Indica nuevos términos, direcciones URL, direcciones de correo electrónico, nombres de archivo y
extensiones de archivo.

Ancho constante
Se usa para listados de programas, así como dentro de párrafos para referirse a elementos del
programa. Nombres de variables o funciones, bases de datos, tipos de datos, variables,
declaraciones y palabras clave.

Ancho constante en negrita


Muestra comandos u otro texto que debe ser escrito literalmente por el usuario.

Ancho constante en cursiva


Muestra texto que debe ser reemplazado por valores suministrados por el usuario o por valores
determinado por el contexto.

Este elemento significa una tip o sugerencia.

Este elemento significa una nota general.

Este elemento indica una advertencia o precaución.

Uso de ejemplos de código

El material suplementario (ejemplos de código, ejercicios, etc.) se puede descargar en


Https://github.com/oreillymedia/knockout_js .

Este libro está aquí para ayudarle a hacer su trabajo. En general, si se ofrece código de ejemplo con
este libro, puede usarlo en sus programas y documentación. Tu no necesita ponerse en contacto con
nosotros para obtener permiso a menos que esté reproduciendo una porción significativa de el
código. Por ejemplo, escribir un programa que utiliza varios trozos de código de este libro no
requiere permiso. Venta o distribución de un CD-ROM de ejemplos

Prefacio | ix
De los libros de O'Reilly requiere permiso. Respondiendo a una pregunta citando esta Libro y
código de ejemplo de cotización no requiere permiso. Incorporando una Cantidad de código de
ejemplo de este libro en la documentación de su producto Requieren permiso.

Apreciamos, pero no necesitamos, la atribución. Una atribución generalmente incluye la título,


autor, editor y ISBN. Por ejemplo: “Knockout.js por Jamie Munro (O'Reilly). Copyright 2015
Jamie Munro, 978-1-491-91431-1. "

Si usted siente que su uso de ejemplos de código cae fuera del uso justo o el permiso dado arriba,
no dude en contactar con nosotros en permissions@oreilly.com .

Libros en línea de Safari®


Safari Books Online es una biblioteca digital bajo demanda que entrega
experto en contenidos tanto en forma de libro y el video de la los principales
autores del mundo en tecnología y negocios.

Profesionales de la tecnología, desarrolladores de software, diseñadores web,


Y los profesionales creativos y de negocios utilizan Safari Books Online como su principal recursos
para la investigación, resolución de problemas, aprendizaje y capacitación en certificación.

Safari Books Online ofrece una gama de planes y precios para la empresa , el gobierno ,la
educación , y los individuos.

Los miembros tienen acceso a miles de libros, videos de capacitación y prepublicación.


Manuscritos en una base de datos completamente buscable de editores como O'Reilly Media,
Prentice Hall Profesional, Profesional de Addison-Wesley, Microsoft Press, Sams, Que, Prensa de
Peachpit, prensa focal, prensa de Cisco, Juan Wiley y los hijos, síntesis, Morgan KaufMann,
Redbooks de IBM, Packt, prensa de Adobe, prensa de FT, Apress, Manning, nuevos
jinetes,McGraw-Hill, Jones & Bartlett, Tecnología de golf, y cientos más . Para más información
sobre Safari Books Online, por favor visítenos en línea .

Cómo contactarnos

Por favor, envíe comentarios y preguntas sobre este libro a la editorial:

O'Reilly Media, Inc.


1005 carretera de gravenstein norte
Sebastopol, CA 95472
800-998-9938 (en los Estados Unidos o Canadá)
707-829-0515 (internacional o local)
707-829-0104 (fax)

X | Prefacio
Tenemos una página web para este libro, donde enumeramos erratas, ejemplos y cualquier
información. Puede acceder a esta página en http://bit.ly/knockoutjs-book .

Para comentar o hacer preguntas técnicas acerca de este libro, envía un correo electrónico
a bookques-tions@oreilly.com .

Para obtener más información sobre nuestros libros, cursos, conferencias y noticias, sitio
en Http://www.oreilly.com .

Encuentranos en Facebook: Http://facebook.com/oreilly


Síguenos en Twitter: http://twitter.com/oreillymedia
Ver en YouTube: http://www.youtube.com/oreillymedia

Expresiones de gratitud
¿Dónde empiezo? Pensé que escribir mi cuarto libro sería mucho más fácil que esoestaba. Mi
último libro se unió tan suavemente, y pensé que esto sería lo mismo. Definitivamente no era! En
cada etapa de este libro, me vi obligado a parar yPensar "¿Por qué estoy haciendo esto de una
manera particular?" Hay tantas cosas que hago conKnockout y JavaScript de cierta manera sin
pensar, que me vi obligado a pensar sobre las razones por las que lo hago de esta manera. Fue una
enorme experiencia de crecimiento personal y me siento como un desarrollador mucho mejor y
espero que estas lecciones se pasan a
todos y cada uno de ustedes.

Primero debo agradecer a mi esposa, Shannon. Ella ayudó inmensamente por ser tan buena mamá a
nuestros tres hijos pequeños -Lily, Owen y Kayla- mientras escribía este libro. Sin su amor y
apoyo, yo no dormiría en absoluto!

En segundo lugar, a Steve Kennedy (usted puede ver su nombre aparece de vez en cuando en
ejemplos). Pasamos muchos una hora de almuerzo pasando detalles minuciosos de casi cada
aspecto del libro. Se aseguró de que cada ejemplo fuera técnicamente preciso y aseguró que el "por
qué" estaba completamente cubierto. Espero que el "por qué" se hagan las cosas será
Inestimable a lo largo de este libro.

En tercer lugar, a Mike Wilson (mejor no intentar la dirección de correo electrónico en el ejemplo
9-4!). Mike realmente Ayudó a dar forma a los primeros capítulos para asegurar que nos dimos
bien a los ejemplos antes de presentar- La teoría. Como lector de muchos libros, esto siempre me
hace sentir mejor ver el resultados del ejemplo "Hello World" inmediatamente.
No puedo olvidar el equipo de O'Reilly. Gracias a Meg, Kara y Gillian por hacer de esto un
experiencia maravillosa.

Y, finalmente, para usted, el lector: Espero que disfrute leyendo este libro y aprenda tanto Como lo
hice de escribirlo!

Prefacio | Xi
CAPÍTULO 1
Introducción a KnockoutJS

KnockoutJS es una biblioteca JavaScript de código abierto. Fue construido para permitirle crear
aplicaciones web dinámicas y ricas. Está construido con el Modelo-Vista-ViewModel (MVVM)
patrón . Knockout hace que sea realmente sencillo implementar un usuario complejo Que responde
a las interacciones del usuario.

Me gusta Knockout porque es una de las bibliotecas JavaScript más livianas disponibles
hoy. Tampoco trata de ser un marco todo en uno. Sirve para un solo propósito: datos vinculando su
ViewModel a su interfaz de usuario.

Implementar Knockout implica tres cosas distintas: una vista que contiene HTML Y elementos
CSS que obtienen datos vinculados a él, un ViewModel que contiene los datos para vincular a la
vista, y decir Knockout para realizar la vinculación de datos a la vista con el ViewModel.

Los ejemplos de este capítulo muestran cómo crear una página HTML con una
ViewModel. Después de revisar la sintaxis básica de vinculación de datos, exploraremos la varips
tipos de ViewModels que funcionan con la vista.

Sintaxis de vinculación de datos (Data Binding Syntax)

El enlace de datos se lleva a cabo mediante la adición de un atributo HTML llamada de datos se
unen a cualquier elemento HTML que desea que Knockout sustituya con información de su
ViewModel.

A veces, una etiqueta HTML no funciona, por lo que Knockout también le permite especificar dato
enlaces con comentarios HTML, como se muestra en el Ejemplo 1-1 .

1
Ejemplo 1-1. Enlaces ocultos usando comentarios HTML

<!-- ko -->
<!-- /ko -->

La vinculación a través de comentarios HTML es extremadamente conveniente cuando se desea


envolver su vinculación alrededor de un bloque de código HTML, o no desea crear un extra objeto
HTML con el único propósito de alojar un enlace de datos.

Ejemplo Data Binding


Una de las cosas más comunes que se utiliza Knockout es mostrar dinámicamente texto o HTML
basado en datos contenidos en un ViewModel.

Ejemplo 1-2 muestra la creación de un encabezado que mostrará la propiedad de name dentro de la
ViewModel.

Ejemplo 1-2. Texto de enlace de datos(Data binding text)

<!DOCTYPE html>
<html>
<head>
<title>Data Binding with KnockoutJS</title>
</head>
<body>
<h1>Hello <span data-bind="text: name"></span></h1>

<script type='text/javascript' src='js/knockout-3.2.0.js'></script>


<script>
var viewModel = function() {
this.name = 'Steve Kennedy';
};

ko.applyBindings(viewModel);
</script>
</body>
</html>

Cuando se ejecuta este ejemplo en un navegador, emite Hello Steve Kennedy en el interior
una etiqueta h1.

Una span span está enlazado a datos a la propiedad nombre del modelo de vista. Esto se hace por
colocar texto: name dentro del atributo bind-data de HTML.

Como se menciona en la introducción a este capítulo, la implementación de Knockout requiere Tres


cosas distintas. La primera es la vista, que en este ejemplo es el HTML que contiene
las etiquetas h1 y span que identifican el enlace de datos (data binding).

2 | Capítulo 1: Introducción a KnockoutJS


El segundo es el ViewModel, que en este ejemplo es la variable JavaScript / función
llamado viewModel que contiene un único nombre de la variable.

El tercero es decir Knockout para realizar la vinculación de datos de la vista y el ViewModel. Esto
se logra llamando a la función ko.applyBindings con un ViewModel.

Cuando se ejecuta esta función, Knockout procesa tanto la vista como la ViewModel. Todos los
enlaces de datos en la vista se ejecutan y se sustituyen dinámicamente por Los datos de
ViewModel.

Knockout no te limita a un solo ViewModel por vista. Grandes proyectos o clases que son bien
diseñados para la reutilización son razones comunes para vincular múltiples ViewModels a una
sola vista. Esto se discute con más detalle en Capítulo 6 .

¿Qué es MVVM?
El patrón de diseño Model-View-ViewModel (MVVM) se basa en gran medida en el Modelo Patrón
de vista controlador (MVC). De hecho, el MV se comparte entre ellos. Es el ViewModel que
realmente separa los dos.

MVVM fue diseñado para implementar la vinculación de datos entre su ViewModel y su vista. Esto
es lo que KnockoutJS hace por nosotros y lo hace muy bien. Se logra utilizando algunos atributos
HTML sencillos de implementar y un JavaScript ViewModel como se muestra en el Ejemplo 1-2 .

Lo más importante a recordar cuando se está construyendo ViewModels es que deben organizarse
para que sea fácil representar cómo usa su vista los datos. Exploramos varios escenarios comunes
en los próximos ejemplos.

Creación de un ViewModel
Un ViewModel puede ser cualquier tipo de variable JavaScript. En el Ejemplo 1-3 Empecemos con
un estructura simple JavaScript que contiene una sola propiedad llamada name.

Ejemplo 1 - 3. Basic ViewModel

var myFirstViewModel = {
name: 'Steve Kennedy'
};

El ejemplo anterior es un escenario perfecto de un ViewModel que podría no correlacionar 100% a


un modelo de datos. Con frecuencia, un modelo de datos separa el nombre y apellido en dos
campos separados para facilitar la edición de los datos. Sin embargo, en una Sentido para
concatenarlos en un solo campo para la exhibición.

Qué es MVVM | 3
El ViewModel anterior es bastante básico. ViewModels no se limitan a una simulación como
demuestran los ejemplos siguientes.

Modelos de vista orientados a objetos

Normalmente, cuando creo ViewModels, creo clases de JavaScript simples o complejas Que me
permiten aprovechar un estilo de programación orientado a objetos (funciones, propiedades,
abstracción, etc.).

Programación Orientada a Objetos con JavaScript

JavaScript es un lenguaje totalmente orientado a objetos (OOP) basado en la


creación de prototipos. No contiene instrucciones de clase como C ++, C #, o
PHP; Sin embargo, las funciones de JavaScript se pueden utilizar para simular
El mismo comportamiento.

También ofrece soporte completo de características de lenguaje OOP como nombre


espacios, objetos, propiedades, herencia, abstracción, etc.

Si es nuevo en JavaScript o en la programación orientada a objetos, Mozilla


Developer Network (MDN) ofrece una gran introductoria artículo .

En el Ejemplo 1-4 , Creo un ViewModel que se construye con algún objeto básico de orientación
Que proporcionará más funcionalidad para nuestra vista.

Ejemplo 1-4. ViewModel orientado a objetos

var mySecondViewModel = function() {


var self = this;

self.name = 'Steve Kennedy';

self.getName = function() {
return self.name;
};
};

En este ejemplo, mi ViewModel es una función de JavaScript que he hecho para actuar como un
clase en OOP. Contiene la misma variable name que he creado en el anterior ejemplo pero esta vez
está encapsulada en mi clase y actúa como una propiedad de ella.

También he añadido una nueva función llamada getNombre que me permite acceder a mi
propiedad de name sin llamar a mi propiedad de clase directamente desde otras partes de mi
código.

4 | Capítulo 1: Introducción a KnockoutJS


self = ¿this?
Es posible que se pregunte por qué la primera línea de mi clase es var self =
this; . Al crear una variable llamada self y asignándole la variable this, me
proporciona una propiedad de mi clase que pueda utilizar dentro de los métodos de
mi clase y fácilmente se refieren a otros métodos o propiedades en mi clase.

Aunque el código resultante entre Ejemplo 1-4 y Ejemplo 1-3 se ven muy diferente de hecho, son
bastante similares. La propiedad name se puede acceder en el exacto misma forma ya sea de
ejemplo, como se muestra en el Ejemplo 1-5 .

Ejemplo 1-5. Acceso a la propiedad name

alert(myFirstViewModel.name);

alert(mySecondViewModel.name); // access the property name


alert(mySecondViewModel.getName()); // access the function getName

Los tres de las declaraciones anteriores le alertará name.

ViewModels con parámetros

En los Ejemplos 1-3 y 1-4 , La propiedad name fue codificado en un valor. En muchos escenarios,
los datos de este tipo se cargarían de una fuente diferente: una base de resultados de una llamada
AJAX, etc.

En el Ejemplo 1-6 , El nombre se rellenará mediante una entrada en ViewModel.

Ejemplo 1-6. Un ViewModel con un parámetro

function ViewModel(name) {
var self = this;

self.name = name;

self.getName = function() {
return self.name;
};
};

var myThirdViewModel = new ViewModel('Steve Kennedy');

En los Ejemplos 1-3 y 1-4 , la clase ViewModel fue asignado directamente a la variable. Este
ejemplo es ligeramente diferente en que una función (que actúa como una clase) llamó View
Model se crea que acepta la propiedad de name. Esta clase es entonces instanciada por pass- El
nombre en el constructor.
ViewModels con parámetros | 5
En todos los ejemplos futuros, seguiré usando las clases de JavaScript ya que proporcionan mucho
más flexibilidad. Sin embargo, si una simple variable o estructura de JavaScript será suficiente para
su opinión, no sienten la necesidad de envolver su ViewModel en una clase.

6 | Capítulo 1: Introducción a KnockoutJS


CAPITULO 2
Los entresijos de la encuadernación de datos (The Ins and Outs os Data Binding)

Datos HTML vinculantes (Binding HTML Data)

En el Ejemplo 1-1 , texto fue datos, unidos a una span tags. Al igual que el texto, es bastante
común contenido de data-bind que contiene datos HTML.

En el Ejemplo 2-1 , La función getName se amplía para envolver la salida del name Enlace de
datos con algún código HTML.

Vinculación de HTML con texto


Los enlaces HTML funcionan de forma similar al texto, con la proporcione cualquier
etiqueta HTML proporcionada. Si se va a utilizar el texto binding contenido HTML, el
código HTML se escaparía y El contenido se mostraría como texto.

En el Ejemplo 21 , actualizo dos cosas. En primer lugar, el atributo de data-bind se mueve de la


lapso de etiqueta para la etiqueta h1. En segundo lugar, se ve obligada a la función getName, que
ha sido Actualizado para contener algún código HTML.

Ejemplo 2-1. Etiqueta h1 con enlace HTML

<!DOCTYPE html>
<html>
<head>
<title>Data Binding with KnockoutJS</title>
</head>
<body>

<h1 data-bind="html: getName()"></h1>

7
<script type='text/javascript' src='js/knockout-3.2.0.js'></script>

<script>
function ViewModel() {

var self = this;

self.name = 'Steve Kennedy';

self.getName = function() {
return 'Hello <em>' + self.name + '</em>!';
};
};

var viewModel = new ViewModel();


ko.applyBindings(viewModel);
</script>
</body>
</html>

Ejecución de Ejemplo 2-1 sería la salida de un mensaje similar al Ejemplo 1-1 , con la excepción
que el nombre estaría en cursiva debido a la etiqueta HTML alrededor de la salida- puesto de
la propiedad name.

Atributos HTML de enlace, clases CSS y estilos CSS

Knockout puede enlazar casi cualquier atributo HTML, clase CSS o estilo CSS. Estos son hecho de
forma bastante similar a los enlaces de text y html con la excepción de que pueda Especifique más
de una propiedad a la vez. Esto se logra envolviendo el Propiedades en corchetes como en
Ejemplos 2-2 y 2-3 .

Ejemplo 2-2. Ejemplo de enlaces CSS y estilos

<p data-bind="
style: { marginBottom: 0, paddingBottom: '1em' }, css: 'myClass'">
This text has custom styles and a CSS class.</p>

El siguiente ejemplo agrega dos estilos personalizados a la etiqueta p. Observe cómo el estilo
propiedades margin-bottom y el paddind-bottom son camelCase en lugar de con guión.

Mediante la colocación de una coma después de la llave de cierre rizado, he añadido un segundo
enlace de datos a la etiqueta p. Esta vez que establece una clase CSS de myClass.

La adición de atributos HTML se realiza de manera similar. En Ejemplo 2-3 , Un atributo id está
agregado.

8 | Capítulo 2: Los entresijos de la vinculación de datos


Ejemplo 2-3. Atributos HTML de vinculación de datos

<p data-bind="attr: { id: 'myCustomId' }">


This p tag has an id data bound to it.</p>

Al igual que en el Ejemplo 2-2 , si quería unirse atributos HTML adicionales, pude colocar una
coma antes de la llave de cierre rizado y agregar propiedades adicionales.

Encuadernación de datos basada en la condición(Condition-Based Data Binding)

Cuando se define un enlace de datos, es posible que desee especificar una condición que cuando
sus datos contienen un valor específico, que genera un nombre de clase diferente. Esto puede ser
logrado mediante la colocación de declaraciones condicionales dentro de su vinculación de datos
(data binding).

En el Ejemplo 2-4 , se crea un botón. El enlace de datos de text contiene un condicional afirmación
de que cuando el ID es 0, el texto del botón dice “Create”. Sin embargo, cuando el id es mayor que
0, dirá “Update”, lo que permite el uso de una sola etiqueta buttom que cambia dinámicamente.

Ejemplo 2-4. Vinculación de datos condicionales

<!DOCTYPE html>
<html>
<head>
<title>Data Binding with KnockoutJS</title>
</head>
<body>

<button type="submit" data-bind="text: (id == 0) ? 'Create' : 'Update'"></button>

<script type='text/javascript' src='js/knockout-3.2.0.js'></script>


<script>
var viewModel = function() {

var self = this;

self.id = 0;
};

ko.applyBindings(viewModel);
</script>
</body>
</html>

Para que este ejemplo funcione, el ViewModel se ha actualizado para incluir una nueva propiedad
llamado id. Al establecerlo en 0, cuando este ejemplo se muestra en un navegador web, el buttom
de texto dirá: “Crear” (create). Con la actualización de la identificación que desea cualquier otro
número positivo, el texto cambiará a decir "Actualizar" (update).

Encuadernación de Datos Basada en Condiciones | 9


Declaraciones de condición
Cuando está definiendo una instrucción condicional en sus datos binding no se requieren
soportes; Sin embargo, me parece mucho más legible. Ayuda a identificar dónde está la declaración
condicional.

Al igual que cuando se utilizan enlaces de datos, los enlaces condicionales se pueden
Utilizando una función en lugar de colocar la condición dentro de la vinculación de datos, como se
muestra en Ejemplo 2-5 .

Ejemplo 2-5. Enlace de datos de condición con una función

<!DOCTYPE html>
<html>
<head>
<title>Data Binding with KnockoutJS</title>
</head>
<body>
<button type="submit" data-bind="text: (isNew()) ? 'Create' : 'Update'">
</button>

<script type='text/javascript' src='js/knockout-3.2.0.js'></script>


<script>
var viewModel = function() {

var self = this;

self.id = 1;

self.isNew = function() {
return id == 0;
};
};

ko.applyBindings(viewModel);
</script>
</body>
</html>

El modelo de vista se actualiza en el ejemplo, para añadir una nueva función llamada isNew. Esta
contiene la misma instrucción condicional que se utilizó en el Ejemplo 2-4 . Los datos la
vinculación también se actualizó para utilizar la función dentro de la instrucción de condición.

El id se actualiza de 0 a 1, por lo que la ejecución de este ejemplo salidas “update” para la buttom
de texto.

10 | Capítulo 2: Los entresijos de la vinculación de datos


CAPÍTULO 3
Entendiendo el contexto de vinculación de datos
-Undestanding the Data binding Context-

Knockout mantiene una jerarquía padre / hijo de contextos. Cuando accedes propiedades a los datos
se enlazan, todo es relativo al contexto en el que se encuentran. La raíz (root) del contexto es el
viewModel que se suministra a la función ko.applyBindings.

Todo es relativo al contexto actual, por lo que los enlaces de text utilizan el name de la propiedad
sin el prefijo del viewModel.

En los siguientes ejemplos, algunos de los nuevos enlaces de datos crean un contexto secundario
bajo el contexto raíz.

Knockout ofrece varias variables útiles que le permiten navegar entre los texto que está en un padre
o incluso el contexto raíz:

$ Root
Esto accede al contexto raíz (el ViewModel está vinculado a Knockout) en cualquier
texto. Esto es útil cuando no está seguro de cuántos contextos parent/ child están por encima
el que está actualmente.

$parent
Cuando se encuentre en un contexto secundario, accederá al padre directo del contexto
actual.

$parents
Esto es similar a la variable $ parent, excepto que contiene una serie de contexto al contexto
en el que se encuentran actualmente en. Los $parents[0] es el mismo
que $parent. Similarmente, usando los $parents [$ parents.length - 1] es lo mismo que
usar $root.

$data
Esto proporciona acceso al objeto actual en el que se encuentra tu contexto. Esto es muy útil
cuando usted está en un contexto que es una variable y no contiene propiedades.

11
$index

Esto sólo está disponible dentro de la unión foreach y contiene un número entero que
representa la posición actual del bucle comenzando en 0 y llegando a la longitud -1.

Para cada Binding

La unión foreach binding se utiliza para repetir una sección de HTML en una matriz de
objetos. En Ejemplo 3-1 , una lista de libros que he escrito se muestra en una lista desordenada.

Ejemplo 3-1. Una lista de libros que usan el enlace foreach

<!DOCTYPE html>
<html>
<head>
<title>Data Binding with KnockoutJS</title>
</head>
<body>
<ul>
<!-- ko foreach: books -->
<li data-bind="text: $data"></li>
<!-- /ko -->
</ul>

<script type='text/javascript' src='js/knockout-3.2.0.js'></script>


<script>
var viewModel = function() {
var self = this;

self.books = [
'Rapid Application Development With CakePHP',
'20 Recipes for Programming MVC 3: Faster, Smarter Web Development',
'20 Recipes for Programming PhoneGap:
Cross-Platform Mobile Development for Android and iPhone'
];
};

ko.applyBindings(viewModel);
</script>
</body>
</html>

Ejemplo 3-1 tarda varias cosas primeras que se han mencionado y los muestra en acción.

El foreach binding no estaba destinada a una etiqueta HTML; En su lugar, se coloca dentro
comentarios HTML. Me parece muy conveniente cuando se usa foreach binding porque evita un
elemento innecesario para envolver el HTML que quiero repetir para cada uno elemento en la
matriz.

12 | Capítulo 3: Comprensión del contexto de vinculación de datos


En el viewModel, una matriz llamada books fue creado. Contiene el nombre de cada libro. Debido a
que no hay propiedades secundarias de acceso dentro de la gama de libros, la etiqueta li es el uso de
la variable de $data que se proporciona a mi contexto child.

Otro uso común para la foreach binding es para mostrar una lista en una tabla HTML.

Ejemplo 3-2 se extiende la matriz books anterior a contener información adicional y


Muéstrela en una tabla.

Ejemplo 3-2. Una tabla de libros usando el enlace foreach

<!DOCTYPE html>
<html>
<head>
<title>Data Binding with KnockoutJS</title>
</head>
<body>
<table>
<thead>
<tr>
<th>Title</th>
<th>ISBN</th>
<th>Published</th>
</tr>
</thead>
<tbody data-bind="foreach: books">
<tr>
<td data-bind="text: title"></td>
<td data-bind="text: isbn"></td>
<td data-bind="text: $parent.formatDate(publishedDate)"></td>
</tr>
</tbody>
</table>

<script type='text/javascript' src='js/knockout-3.2.0.js'></script>


<script>
function ViewModel() {
var self = this;

self.books = [
{
title: 'Rapid Application Development With CakePHP', isbn: '1460954394',
publishedDate: '2011-02-17'
}, {
title: '20 Recipes for Programming MVC 3: Faster, Smarter Web
Development',
isbn: '1449309860', publishedDate: '2011-10-14'
}, {

foreach Binding | 13
title: '20 Recipes for Programming PhoneGap:
Cross-Platform Mobile Development for Android and iPhone', isbn: '1449319548',
publishedDate: '2012-04-06'
}
];

self.formatDate = function(dateToFormat) {
var months = new Array("January", "February", "March", "April", "May", "June", "July",
"August", "September", "October", "November", "December");

var d = new Date(dateToFormat);


return months[d.getMonth()] + ' ' + d.getDate() + ', ' +
d.getFullYear();
};
};

var viewModel = new ViewModel();


ko.applyBindings(viewModel);
</script>
</body>
</html>

Ejemplo 3-2 introduce varias nuevas cosas así. En primer lugar, el modelo de vista ya no es siendo
asignado directamente a una variable. Ahora se crea una instancia como una clase, y luego pasa la
variable resultante a la ko.applyBindings función.

La books array se ha ampliado para contener múltiples propiedades: title, isbn , y


publishedDate . Estas propiedades son entonces unidos en el td etiqueta dentro de la tabla. Observe
cómo el foreach binding ha cambiado el contexto sea en relación con los books array las
propiedades están siendo alcanzados por su nombre sin hacer referencia a los books array.

La “fecha de publicación” td etiqueta es ligeramente diferente a los dos anteriores. Siendo los datos
vinculados (databound) a una función que acepta una fecha como parámetro. Debido a que la
función no está contenida dentro del books array, se necesita acceder mediante el $parent variables
porque está fuera del contexto actual.

14 | Capítulo 3: Qué el contexto Enlace de datos (Data Binding Context)


$ parent Versus $root

En el ejemplo anterior, $parent es intercambiable entre $root y $parents[0] . He


experimentado que, aunque $root proporciona acceso rápido a los parent más alta, es mejor
utilizar el relative-based, más $parent o $parents array.

Como código se refactorizado en el futuro, puede encontrarse con que su existente HTML se
reutiliza en un contexto diferente, donde $root puede no ser lo mismo que $parent, y esto le
da menos posibilidades de acceder a la contexto equivocado.

Foreach Callbacks (Eventos)

El foreach unión contiene varios métodos de devolución de llamada que pueden ser ejecutadas por
Knockout después de ciertos eventos ocurren:

afterRender
Esto se llama cuando el foreach termina primero inicializar y cada vez es un elemento
añadido a la matriz.

afterAdd
Esto se llama cada vez que se añade un elemento al a array. A diferencia de afterRender,
esto es no se llama cuando el array se inicializa primero.

beforeRemove
Esto se llama cuando un elemento se retira del array. Esto se utiliza a menudo para animar
una la eliminación de un elemento.

beforeMove
Esto se llama cuando un elemento se mueve dentro del array. Es otra gran oportunidad para
comenzar una animación o añadir un efecto al elemento que se está interactuado con.

afterMove
Esto se llama después de que el elemento se mueve dentro del array. Al igual que
el beforeMove, este sería la oportunidad de terminar cualquier efecto sobre el elemento
movido.

Para demostrar cómo se utiliza una de estas devoluciones de llamada, en el Ejemplo 3-3 , voy a
mejorar Ejemplo 3-2 para incluir una imagen. El afterRender buscará la imagen del servidor para
ayudar a prevenir que el bloqueo de la página que se queden mientras se carga una imagen.

Ejemplo 3-3. Cargar la imagen con la devolución de llamada afterRender

<!DOCTYPE html>
<html>
<head>
<title>Data Binding with KnockoutJS</title>
</head>

15 | foreach Callbacks
<body>

<table>
<thead>
<tr>
<th>Thumbnail</th>
<th>Title</th>
<th>ISBN</th>
<th>Published</th>
</tr>
</thead>
<tbody data-bind="foreach: { data: books, afterRender: loadImage }">
<tr>
<td><img src="images/loading.gif" data-bind= "attr { id: 'image_' +
isbn }" /></td>
<td data-bind="text: title"></td>
<td data-bind="text: isbn"></td>
<td data-bind="text: $parent.formatDate(publishedDate)"></td>
</tr>
</tbody>
</table>

<script type='text/javascript' src='js/jquery.js'></script>


<script type='text/javascript' src='js/knockout-3.2.0.js'></script>
<script>
function ViewModel() {
var self = this;

self.books = [
{
title: 'Rapid Application Development With CakePHP', isbn: '1460954394',
publishedDate: '2011-02-17', image:
'http://ecx.images-amazon.com/images/I/41JC54HEroL._AA160_.jpg'
}, {
title: '20 Recipes for Programming MVC 3: Faster, Smarter Web
Development',
isbn: '1449309860', publishedDate: '2011-10-14',
image:
'http://ecx.images-amazon.com/images/I/51LpqnDq8-L._AA160_.jpg'
}, {
title: '20 Recipes for Programming PhoneGap:
Cross-Platform Mobile Development for Android and iPhone', isbn: '1449319548',
publishedDate: '2012-04-06', image:
'http://ecx.images-amazon.com/images/I/51AkFkNeUxL._AA160_.jpg'
}
];

16 | Capítulo 3: Qué el contexto Enlace de datos


self.loadImage = function(element, index, data) {
$('#image_' + index.isbn).attr('src', index.image);
};

self.formatDate = function(dateToFormat) {
var months = new Array("January", "February", "March", "April", "May", "June", "July",
"August", "September", "October", "November", "December");

var d = new Date(dateToFormat);


return months[d.getMonth()] + ' ' + d.getDate() + ', ' +
d.getFullYear();
};
};

var viewModel = new ViewModel();


ko.applyBindings(viewModel);
</script>
</body>
</html>

Ejemplo 3-3 es bastante similar al Ejemplo 3-2 en términos de estructura, con varios importantes
cambios.

En primer lugar, los foreach binding se ha modificado para incluir el afterRender y volverlo a
llamar. Se llamará a la función loadImage.

En segundo lugar, la tabla se actualiza para incluir una nueva columna para la thumbnail
(miniatura). Por defecto, Pongo una imagen de marcador de posición en su lugar. Luego, utilizando
un atributo binding (enlace), puse la identificación de la imagen para incluir un identificador único
que se utilizará para intercambiar la imagen.

En tercer lugar, la matriz(array) books fue actualizado para incluir una nueva propiedad
llamada image que contiene la ruta completa a la imagen del libro en Amazon.

Y, por último, la loadImage se creó función. Este utiliza un poco de jQuery para el acceso la
imagen por el ID único que creó, y cambia la fuente de la imagen a la ubicación proporcionada en
el libros de matriz (books array).

Para que este ejemplo funcione correctamente, es necesario descargar la librería jQuery . Una vez
descargado, lo coloca dentro de la creada anteriormente js carpeta. Por simplicidad, renombrado el
archivo jquery.js. No dude en dejarlo como está, pero asegúrese de actualizar la ubicación al
archivo en el código HTML.

with Binding
El with binding es similar a la foreach binding ya que crea un nuevo child (hijo) contexto. Todo
dentro de la vinculación (binding) es ahora en relación con la variable a la cual es ligado. La
principal diferencia entre los dos es que con es un objeto individual con multiples propiedades,
mientras que el foreach repite el HTML en la unión (binding).
with Binding | 17
Ejemplo 3-4 amplía la lista de libros(books) para ser una sola página de detalles del libro y
apalancamientos el de la unión (binding) para evitar que los prefijos repetitivos de la variable libro
(book).

Ejemplo 3-4. Libro detalla el uso de la unión con

<!DOCTYPE html>
<html>
<head>
<title>Data Binding with KnockoutJS</title>
</head>
<body>
<div id="book" data-bind="with: book">
<h1 data-bind="text: title"></h1>
<h2>Published on <span data-bind="text: $parent.formatDate(publishedDate)">
</span></h2>
<p data-bind="text: synposis"></p>
</div>

<script type='text/javascript' src='js/knockout-3.2.0.js'></script>


<script>
function ViewModel(book) {
var self = this;

self.book = book;

self.formatDate = function(dateToFormat) {
var months = new Array("January", "February", "March", "April", "May", "June", "July",
"August", "September", "October", "November", "December");

var d = new Date(dateToFormat);


return months[d.getMonth()] + ' ' + d.getDate() + ', ' +
d.getFullYear();
};
};

var book = {
title: 'Rapid Application Development With CakePHP', synposis: '...',
isbn: '1460954394', publishedDate: '2011-02-17'
};

var viewModel = new ViewModel(book);

ko.applyBindings(viewModel);
</script>
</body>
</html>

18 | Capítulo 3: Qué el contexto Enlace de datos


En este ejemplo, un libro (book) se crea objeto que contiene algunas propiedades adicionales que
no están disponibles en la página del listado de libros (books). Esto se pasa al constructor de la
ViewModel y se utiliza dentro de la unión (with binding).

A diferencia del foreach vinculante (binding), el HTML no se repite, ya que sólo hay un solo book
para mostrar.

En comparación, como se muestra en el ejemplo 3-5 , si usted no ha utilizado el with data binding,
el mismo resultado podría lograrse con el siguiente código HTML.

Ejemplo 3-5. detalles del libro sin la unión con

<div id="book">
<h1 data-bind="text: book.title"></h1>
<h2>Published on <span data-bind="text: formatDate(book.publishedDate)">
</span></h2>
<p data-bind="text: book.synposis"></p>
</div>

Cuando with binding (la unión, el enclace) no se utiliza, observe cómo los elementos HTML son
enlazados a datos utilizando la ruta de la variable completa (por ejemplo, book.title ). La otra
diferencia es que el formato Fecha de la función ya no tiene que llevar el prefijo $ parent debido a
un contexto hijo (child) no fue creado, y todavía lo es en el contexto de la raíz (root).

En situaciones como esta, utilizando with binding (la unión, el enclace) es un simple conveniencia
de evitar repetición y anteponiendo las propiedades secundarias con la variable.

With binding | 19
CAPÍTULO 4
Modificación de las propiedades dinámicamente

Hasta ahora, sólo hemos tocado en un pequeño trozo del framework KnockoutJS. En este capítulo,
vamos a empezar a tomar ventaja de las propiedades que cambian dinámicamente a través la
interacción del usuario.

Knockout llama a estas propiedades observables. Cuando se define una variable o propiedad como
un observable, Knockout rastrea cuando cambia. Esto se puede utilizar en una variedad de
diferentes formas que veremos en los próximos capítulos.

La definición de un observable

Hay tres tipos diferentes de observables que se utilizan con mayor frecuencia. El primero, se
muestra en el Ejemplo 4-1 , Es una variable observable.

Ejemplo 4-1. La creación de una variable observable

var myObservable = ko.observable();


myObservable('Hello');

alert(myObservable());

Para crear un observable, asignar la funcion ko.observable de la variable. por defecto un valor se
puede especificar en el constructor de la llamada. Knockout luego se convierte en sus variables en
una función y pistas cuando cambia el valor, con el fin de notificar a la interfaz de usuario
elementos asociados con la variable.
21
Accesanco a un Observable

Después de definir un observable, tiene que ser llamado como una función con el fin de
obtener o establecer su valor. Si intenta establecer directamente como si se eran una
variable, lo observable serían destruidos.

En Ejemplo 4-1 , observe cómo el valor de la variable se realiza como una función. Al
acceder al valor de la alerta declaración, esto es también hecho. Si se va a tratar de alertar al
observables sin los soportes, se emitiría una gran porción de código JavaScript que contiene
la implementación de la función observable Knockout.

El segundo tipo de observable, como se muestra en el Ejemplo 4-2 , Es una matriz observable
(observable array).

Ejemplo 4-2. Creación de una matriz observable

var myObservableArray = ko.observableArray([]);


myObservableArray.push('Hello');

En el Ejemplo 4-2 , la matriz se instancia como una matriz vacía haciendo pasar dos cuadrados
soportes en el constructor.

Al igual que las variables observables, cuando se añaden o eliminan elementos de la matriz,
Knockout notifica elementos que se ha suscrito a las notificaciones.

Una matriz observable es ideal para uso con mesas donde los elementos están siendo
dinámicamente añadido y eliminado.

El último tipo de observable, como se muestra en el Ejemplo 4-3 , es un observable


computarizada . Esta es ligeramente diferente a los dos tipos anteriores en que una calculada
observable es comúnmente utilizado para combinar uno o más observables en un solo objeto.

Ejemplo 4-3. Creación de una computa observables

self.firstName = ko.observable('Steve');
self.lastName = ko.observable('Kennedy');

self.fullName = ko.computed(function() {
return 'Hello ' + self.firstName() + ' ' + self.lastName();
});

Una vez que el modelo de vista está obligado a Knockout, se ejecuta la función computarizada. Por
cada uno puede observar que se utiliza dentro de esa función, se suscribe a ningún cambio a
eventos Esa variable. Cuando se cambia, Knockout sabe que la variable calculada debe estar
actualizada.

22 | Capítulo 4: Propiedades cambia dinámicamente


pureComputed observables
A partir del knockout versión 3.2 (que utiliza este libro), un nuevo tipo de llama observables
pureComputed observable fue introducido (ver Ejemplo 4-4 ). Es muy similar a la
computed observables con varias mejoras de rendimiento y memoria. Los nombre está tomado de
la pure function término de programación.

Un ejemplo común de cuándo utilizar este tipo de observable es en el Ejemplo 4-3 , donde el
nombre y apellido se concatenan en un computed observables.

Ejemplo 4-4. La creación de un pureComputed observables

self.firstName = ko.observable('Steve');
self.lastName = ko.observable('Kennedy');

self.fullName = ko.pureComputed(function() {
return 'Hello ' + self.firstName() + ' ' + self.lastName();
});

Los observables son tratados de manera diferente porque cuando no hay elementos de escucha de
cambios en la variable calculada, que se colocan en dormir modo versus listening modo. Mientras
que en el modo de dormir, Knockout dispone todas las dependencias y reevalúa el contenido
cuando se escucha a diferencia de modo de lectura, que gestiona las referencias a todos los
suscriptores y asegura el valor es de hasta al día antes del primer acceso.

El ejemplo anterior se deduce ambas reglas para ser una pura función:

• Dada la misma entrada, la salida será el mismo resultado.


• No tiene efectos secundarios se producen a causa de la función ejecutada.

La segunda regla es probablemente el más importante a la hora de decidir si se debe utilizar una
computed o una pureComputed observable con Knockout. Dentro de su observables, si necesita
ejecutar otro código, a continuación, se debe utilizar un observables calculada para asegurar que
esté en modo de escucha en lugar del modo de dormir. Vea la diferencia en Ejemplo 4-5 .

Ejemplo 4-5. Computarizada frente pureComputed

<!DOCTYPE html>
<html>
<head>
<title>Data Binding with KnockoutJS</title>
</head>
<body>

<script type='text/javascript' src='js/knockout-3.2.0.js'></script>


<script>
function ViewModel() {
var self = this;

pureComputed observables | 23
self.firstName = ko.observable('Steve');
self.lastName = ko.observable('Kennedy');

self.pureComputedExecutions = 0;
self.computedExecutions = 0;

self.pureComputedFullName = ko.pureComputed(function() {
// This is not recommended
self.pureComputedExecutions++;
return 'Hello ' + self.firstName() + ' ' + self.lastName();
});

self.computedFullName = ko.computed(function() {
self.computedExecutions++;
return 'Hello ' + self.firstName() + ' ' + self.lastName();
});
};

var viewModel = new ViewModel();


ko.applyBindings(viewModel);

alert('Pure computed executions: ' + viewModel.pureComputedExecutions);


alert('Computed executions: ' + viewModel.computedExecutions);
</script>
</body>
</html>

Ejemplo 4-5 rompe la regla pura función mediante la colocación de un efecto secundario dentro de
ella; sin embargo, que está demostrando la diferencia entre el la espera (sleep) y el modo de
escucha.

Cuando se ejecuta este ejemplo, después de que el ViewModel se ha unido a la vista, dos alerta los
mensajes se muestran que muestran el número de veces que las funciones pureComputed y
Computed se llaman.

Debido a que los computed observables no están enlazados a datos (data-bound), el pureComputed
está en el modo de dormir (sleep); por lo tanto, la función computed nunca ha sido visitada, y el
contador está a 0. Sin embargo, la función computed se evalúa automáticamente en los data-
binding (dato vinculante) para establecer sus oyentes, haciendo que el contador se incremente a 1.

Si se va a data-bind a ambos observables computed, que se traduciría en tanto contadores siendo 1


porque la pureComputed está ahora en modo de escucha, ya que tiene un suscriptor.

Cómo mostrar y ocultar elementos (Showing and Hiding Elements)


Ejemplo 4-6 combina el uso de una variable observable con un enlace de datos nuevos
llama visible . El visible enlace (data binding) establece la propiedad display de CSS de datos
en pantalla a cualquiera bloque o ninguno, dependiendo de los resultados de la condición utilizada
en la unión (binding).

24 | Capítulo 4: Propiedades cambia dinámicamente


En el Ejemplo 4-6 , cuando el botón es presionado por el usuario, se dispara la variable observable
para cambiar, lo que hace que la interfaz de usuario para mostrar los elementos HTML que estan
previamente ocultos.

Ejemplo 4-6. Usando el enlace de datos (data-bind) visible con un observable

<!DOCTYPE html>
<html>
<head>
<title>Data Binding with KnockoutJS</title>
</head>
<body>
<button type="button" data-bind="click: updateObservable">Click me</button>

<div data-bind="visible: showExtraData" style="display: none">


Now you see me!
</div>

<script type='text/javascript' src='js/knockout-3.2.0.js'></script>


<script>
function ViewModel() {
var self = this;

self.showExtraData = ko.observable(false);

self.updateObservable = function() {
self.showExtraData(!self.showExtraData());
};
};

var viewModel = new ViewModel();


ko.applyBindings(viewModel);
</script>
</body>
</html>

Vamos a empezar con el código HTML. Un buttom se crea y datos enlazados a un evento, haga
click. Cuando el usuario hace clic en el botón, Knockout llama a la función updateObservable.

Debajo del buttom es un div etiqueta. Este elemento está utilizando el visible data binding. Cuando
el resultado de la condición es false, el elemento está oculto. Cuando se cambia a true, se hace
visible.

Cómo mostrar y ocultar elementos | 25


Estilo CSS display: none
Usted puede notar que en el HTML anterior, también agregué style = "display: none" a la
div. Hice esto para evitar un parpadeo cuando se carga la página. ocurriría debido a un
parpadeo sin el display: none estilo, el usuario vería temporalmente el contenido dentro de
la div elemento. Cuando los enlaces Knockout ejecutar, el elemento se oculta.

A continuación, vamos a explorar el ViewModel de JavaScript. La primera cosa que hice fue crear
una variable observable llamada showExtraData y por defecto a false (oculto por defecto). Y, por
último, la updateObservable se define la función, que llama cuando Knockout se hace clic en el
botón. Esta función simplemente inversa al valor de la variable showExtraData. La primera vez que
se hace clic en el botón, el código HTML dentro del div se revela. Si se hace clic de nuevo, el texto
se oculta de nuevo.

Adición y eliminación de elementos


Los if y ifnot data-binding son bastante similares a los anteriores visible la data binding. La
diferencia entre los dos es que, a diferencia de la visible binding estableciendo un style CSS para
mostrar u ocultar el elemento, if y ifnot físicamente añade o quitar el elemento desde el Document
Object Model (DOM). Ver Ejemplo 4-7 .

Ejemplo 4-7. Si el enlace de datos con un observable

<!DOCTYPE html>
<html>
<head>
<title>Data Binding with KnockoutJS</title>
</head>
<body>
<button type="button" data-bind="click: updateObservable">Click me</button>

<!-- ko if: showExtraData -->


<div>
Now you see me!
</div>
<!-- /ko -->
<script type='text/javascript' src='js/knockout-3.2.0.js'></script>
<script>
function ViewModel() {
var self = this;

self.showExtraData = ko.observable(false);

self.updateObservable = function() {

26 | Capítulo 4: Propiedades cambia dinámicamente


self.showExtraData(!self.showExtraData());
};
};

var viewModel = new ViewModel();


ko.applyBindings(viewModel);
</script>
</body>
</html>

Ejemplo 4-7 es casi idéntico al Ejemplo 4-6 ; De hecho, el viewModel es JavaScript Sin tocar El
HTML contiene la ligera diferencia. En lugar de la div que contiene un data para los data binding
para visible, he utilizado el if de los data binding dentro de un comentario HTML.

Si inspecciona el código HTML cuando se carga la página, se dará cuenta de que la div etiqueta es
por ningún lado. Al hacer clic en el botón hará que el código HTML para ser dinámicamente
insertado en el DOM.

Adición y eliminación del DOM

Si se añade un elemento a la DOM a causa de un usuario interacción que tenían uno o más
efectos secundarios de JavaScript realizan en él durante la carga de la página original, éstos
tendrían que ser ejecutado después de añadir de nuevo a la DOM.

Por ejemplo, si usted tiene un campo que está vinculado a una fecha jQuery Picker,
JavaScript es necesario para inicializarlo. Esto tiene que ser ejecutado después se añade el
elemento al DOM.

En un escenario de este, que podría ser más prudente utilizar el visible el enlace de datos
(data-binding) debido a que los elementos permanecerán en el DOM y puede ser inicializado
en la carga del documento.

Encuentro utilizando los if y ifnot data-binding muy conveniente cuando quiero quitar contenido
que sólo quiero que el usuario sea capaz de acceder en escenarios específicos.

Ejemplo 4-8. el enlace de datos ifnot

ifnot: !showExtraData()

Ejemplo 4-7 podría lograrse usando ifnot mediante la actualización de la unión (binding) para
invertir la condición. En lugar de añadir los elementos cuando la condición se evalúa como true,
la ifnot añade los elementos cuando la condición se evalúa como false.

Adición y eliminación de elementos | 27


El uso de soportes y observables

En caso de que no se dio cuenta, en el ejemplo anterior, tenía que ejecutar


la showExtraData observables como si fuera una función para la adición soportada al
final. Todos los ejemplos anteriores no requirieron los soportes porque Knockout
intuitivamente sabía como ejecutar la observable; sin embargo, porque he añadido el ! al
estado cuando es falso, tuvimos que decir a Knockout para ejecutar la observable y luego
aplicar el !declaración.

En el Capítulo 2 , Al demostrar datos condicionales de unión (data-binding), si el


Id propiedad eran un observable, el ejemplo se actualice como sigue: id () == 0, lo que
indica a Knockout para ejecutar la identificación observable y poder compararlo con el valor
0.

Cuándo utilizar observables


Observables son extremadamente potentes, y como Peter Parker es contada por su tío Ben: “Un
gran poder conlleva una gran responsabilidad.” (Sí, debo utilizar siempre este Spider man cuando
este sale.)

Cuando un observable se define primero, Knockout inicializa la variable como una función de
realizar un seguimiento de los cambios en el valor. Este contiene una cierta cantidad de
procesamiento gastos generales.

Es importante tomar decisiones conscientes sobre lo que usted define como un observable. Si el
valor puede cambiar potencialmente (mediante programación o de la interacción del usuario), una
propiedad observable es completamente válido. Sin embargo, si tiene cuatro propiedades y sólo dos
de ellos va a cambiar, y las otras propiedades son necesarias pero nunca cambiar, no hay necesidad
para que sean observados (ver Ejemplo 4-9 ).

Ejemplo 4-9. Objeto que se observa parcialmente

function ViewModel(person) {
var self = this;

self.person = {
id: person.id,
firstName: ko.observable(person.firstName), lastName:
ko.observable(person.lastName), status: person.status
};
};

var person = {
id: 1,
firstName: 'Steve', lastName:
'Kennedy',

28 | Capítulo 4: Propiedades cambia dinámicamente


status: 'Active'
};

var viewModel = new ViewModel(person);


ko.applyBindings(viewModel);

Ejemplo 4-9 implica que sólo puedo cambiar el nombre y apellido de un objeto person. La id y el
status no se puede cambiar; Sin embargo, todavía se pueden utilizar en Knockout el enlace de datos
(data-binding) con fines de exhibición.

Cuándo utilizar observables | 29


CAPÍTULO 5
Trabajando con formularios

Formulario de datos de enlaces (data-binding)


Knockout Proporcionar varias binding para trabajar con elementos de formularios específicos.

• El value de binding se utiliza con input, select y textarea de entrada (inputs).


• El textInput binding también se utiliza con input y el textarea y es bastante similar al value de
binding. Cuando el textInput se utiliza, los cambios observables con cada interacción con el
usuario, en oposición al value de binding, que por defecto es actualizable cuando cambia la
forma de elementos (por lo general cuando el campo pierde el foco). Ver Ejemplo 5-1 .
• El checked binding se utiliza con casillas de verificación y botones de radio.
• Las options de binding se utiliza en el select de entrada de formulario para rellenar la lista de
opciones disponibles en el drop-down de la lista.
• Los selectedOptions binding también se usa con el select de entrada de formulario; Más
específicamente cuando se utiliza una lista de selección múltiple. Esto se ve obligada
comúnmente a una array observable, en contraposición a una variable observable.
• La enable y el disable binding trabaja con todas las entradas de formulario para habilitar o
desactivar el elemento de formulario cuando la condición da lugar a verdadero o falso,
respectivamente.

Todos estos enlaces (binding) Knockout lo llama two-way binding. Esto significa que cuando se
cambia el elemento de formulario, su propiedad ViewModel se actualiza y si cambias mediante
programación la observable, el elemento de binding se engancha a ser actualizado
automáticamente.

31
Ejemplo 5-1. La unión textInput

<!DOCTYPE html>
<html>
<head>
<title>Data Binding with KnockoutJS</title>
</head>
<body>

<textarea data-bind="textInput: myText"></textarea><br/>


Characters remaining: <span data-bind="text: charactersRemaining"></span>

<script type='text/javascript' src='js/knockout-3.2.0.js'></script>


<script>
function ViewModel() {
var self = this;

self.myText = ko.observable('');

self.maxCharacters = 140;

self.charactersRemaining = ko.computed(function() {
return self.maxCharacters - self.myText().length;
});
};

var viewModel = new ViewModel();


ko.applyBindings(viewModel);
</script>
</body>
</html>

Este ejemplo utiliza el textInput de binding en un textarea. Esto se une a la myText


observable. Justo debajo del cuadro de texto, se muestran los caracteres restantes totales. Un
elemento span se une a una llamada computed observables llamados charactersRemaining. En
cada momento en que la propiedad de myText se cambia, esta función se llama y actualiza
automáticamente los restantes caracteres.

Si se va a probar el ejemplo anterior pero sustituyendo el textInput el binding con el


valor vinculante (binding), que no vería actualiza con cada pulsación de tecla del recuento de
caracteres.

Esto se puede solucionar porque el value de la binding ofrece una propiedad secundaria llamada
valueUpdate . Esto se puede ajustar a cualquier otro evento que Knockout debe escuchar en para
desencadenar cambios.

Ejemplo 5-2 dice Knockout para actualizar el valor cuando el evento afterkeydown es activado, que
proporcionará actualizaciones más instantáneas. Por defecto, cuando se utiliza el valor vinculante
(binding), Knockout solamente está detectando el onchange evento.

32 | Capítulo 5: Trabajo con formularios


Ejemplo 5-2. Utilizando el valor de la unión con una valueUpdate

<textarea data-bind="value: myText, valueUpdate: 'afterkeydown'"></textarea><br/>

Aconsejo precaución en el cambio de la opción valueUpdate. Mientras que las actualizaciones


instantáneas son agradables, muchos escenarios no requieren esto. Si está actualizando Knockout
innecesariamente observables, rendimiento de las aplicaciones van a sufrir.

Vinculaciones de datos de eventos


En el Capítulo 4 , se utilizó el primer suceso de unión Knockout (click). Knockout ofrece varios
otros eventos que se utilizan comúnmente con las formas:

 La submit que la unión (binding) se utiliza en la form del elemento y se activa cuando una
forma es presentada.
 El click de unión (binding) se utiliza comúnmente en los botones y enlaces, pero se puede
aplicar a cualquier elemento DOM que es visible.
 El hasFocus unión (binding) se utiliza comúnmente en la entrada de elementos y está
activado cuando el elemento DOM recibe la atención al usuario.
 El event de unión permite especificar cualquier otro evento DOM (incluyendo el hacer
click y submit fijaciones), como al pasar el mousemover, keypress, etc.

Ejemplo 5-3 es bastante similar a un ejemplo anterior donde se visualiza una lista de libros del
array. El ejemplo se ha reducido hasta sólo incluir un título y una imagen.

Ejemplo 5-3. Un evento mouseover

<!DOCTYPE html>
<html>
<head>
<title>Data Binding with KnockoutJS</title>
</head>
<body>

<img id="current_book" />

<ul>
<!-- ko foreach: books -->
<li data-bind="text: title, event: { mouseover: $parent.loadImage }"></li>
<!-- /ko -->
</ul>

<script type='text/javascript' src='js/jquery.js'></script>


<script type='text/javascript' src='js/knockout-3.2.0.js'></script>
<script>
function ViewModel() {
var self = this;

Datos de eventos enlaces | 33


self.books = [
{
title: 'Rapid Application Development With CakePHP', image:
'http://ecx.images-amazon.com/images/I/41JC54HEroL._AA160_.jpg'
}, {
title: '20 Recipes for Programming MVC 3: Faster,
Smarter Web Development',
image:
'http://ecx.images-amazon.com/images/I/51LpqnDq8-L._AA160_.jpg'
}, {
title: '20 Recipes for Programming PhoneGap:
Cross-Platform Mobile Development for Android and iPhone', image:
'http://ecx.images-amazon.com/images/I/51AkFkNeUxL._AA160_.jpg'
}
];

self.loadImage = function(data, event) {


$('#current_book').attr('src', data.image);
};
};

var viewModel = new ViewModel();


ko.applyBindings(viewModel);
</script>
</body>
</html>

Los libros se imprimen en una lista desordenada. El li elemento se une a la de la propiedad del
title. Después de que el text binding, un event de unión(binding) se utiliza y se detecta el mouse
over event.

Cuando se activa este evento, llama a la función loadImage. Esta función llena una imagen con el
valor de la image de la propiedad.

Escuchar los Cambios


Cuando se introdujeron los observables, he mencionado cómo Knockout envía eventos que
notificar a los oyentes sobre los cambios en el valor. Knockout proporciona una utilidad de
suscripción la función que puede ser añadido a un observable. Entonces, cada vez que el observable
es cambiado, se invoca la función, lo que permite a los cambios programáticos que observables.

La suscripción a los observables le permite realizar fácilmente los efectos secundarios a otra
propiedades en su modelo de vista cuando cambia el valor de la propiedad (ver Ejemplo 5-4 ). Los
ejemplo siguientes muestran esto con dos listas desplegables. Cuando el observado valor a partir de
los primeros cambios de la lista, que se actualiza de forma dinámica el valor observado de la
segunda lista.

34 | Capítulo 5: Trabajo con formularios


El primer ejemplo contiene una lista de países a elegir. Se utiliza el anteriormente enlaces de datos
mencionados para rellenar un select opción elemento, definir el valor utilizado Para el texto, y
definir los valores en la lista drop-down.

La segunda lista contiene los estados disponibles para el país seleccionado. Por defecto, esta se se
oculta hasta que se seleccione un país que contiene una matriz de estados.

Ejemplo 5-4. Suscribirse a un cambio de valor observable

<!DOCTYPE html>
<html>
<head>
<title>Data Binding with KnockoutJS</title>
</head>
<body>

<select data-bind="options: availableCountries, optionsText: 'name',


optionsValue: 'id', optionsCaption: 'Select a country...',
value: selectedCountry"></select>

<select data-bind="options: availableStates, optionsText: 'name', optionsValue: 'id',


value: selectedState, visible: availableStates().length >
0" style="display:none"></select>

<script type='text/javascript' src='js/knockout-3.2.0.js'></script>


<script>
function ViewModel() {
var self = this;

self.selectedCountry = ko.observable();
self.selectedState = ko.observable();

self.availableCountries = ko.observableArray([
{
id: 1, name: 'United States', states: [
{ id: 1, name: 'Alabama' },
// ...
]
},
{
id: 2, name: 'Canada', states: [
{ id: 53, name: 'Alberta' },
// ...
]
}
]);

self.availableStates = ko.observableArray([]);

self.selectedCountry.subscribe(function() {
self.availableStates([]);

Escuchar los cambios | 35


for (var i = 0; i < self.availableCountries().length; i++) {
if (self.availableCountries()[i].id == self.selectedCountry()) {
self.availableStates(self.availableCountries()[i].states); break;
}
}
});

};

var viewModel = new ViewModel();


ko.applyBindings(viewModel);
</script>
</body>
</html>

El conjunto de los países se llena con una id, name, y la propiedad del states. El array de estados se
utilizan para rellenar los estados disponibles cuando se selecciona ese país de la lista dwop-down.

Esto se logra mediante la suscripción a la selectedCountry observable a la que la primera lista


dwop-down es obligada. Cuando cambia el valor, la función dentro de subscribe se llama. Esta
función borra la lista de los Estados y, a continuación, coloca a través de la países disponibles hasta
que encuentra el país seleccionado (comparando el id de propiedad de
la availableCountry valor). Cuando se encuentra una coincidencia, los estados están disponibles
poblado.

Discreta validación de formularios


Un capítulo dedicado a trabajar con formas no estaría completo si no cubría validacion de los datos
dentro de un formulario. Validación de forma común incluye garantizar que de datos requerida se
rellena antes de presentar. También puede incluir la validación del contenido basado en el tipo de
datos; por ejemplo, una dirección de correo electrónico requiere muy específica formateo.

Mediante la implementación de la validación discretas formas, todo esto puede ocurrir antes de la
forma siendo presentado. Hay un excelente plugin de jQuery que permite una fácil adición de
validación del lado del cliente discreta.

Ejemplo 5-5 muestra cómo configurar y incluir varios une forma de datos simples, incluyendo
el submit enlazar datos (data binding).

Para empezar, visite el jQuery Validación Plugin página web y descargar el plugin. Esta ejemplo
utiliza versión 1.13. Este plugin también requiere la biblioteca jQuery para ser instalado. Esto
puede ser descargado desde el sitio de jQuery o la lib carpeta en la validación de jQuery descarga.

36 | Capítulo 5: Trabajo con formularios


Copiar estos dos archivos en la creada anteriormente js carpeta en la biblioteca Knockout
actualmente reside.
Ejemplo 5-5. la validación de formularios Unobtrusive

<!DOCTYPE html>
<html>
<head>
<title>Data Binding with KnockoutJS</title>
</head>
<body>

<form data-bind="submit: validate">


<input type="text" data-bind="value: firstName" placeholder="Enter
your first name" required /><br/>
<input type="text" data-bind="value: lastName" placeholder="Enter
your last name" required /><br/>
<input type="email" data-bind="value: email"
placeholder="Enter your email" required /><br/>
<input type="submit" />
</form>

<script type='text/javascript' src='js/jquery.js'></script>


<script type='text/javascript' src='js/jquery.validate.min.js'></script>
<script type='text/javascript' src='js/knockout-3.2.0.js'></script>
<script>
function ViewModel() {
var self = this;

self.firstName = ko.observable(); self.lastName =


ko.observable(); self.email = ko.observable();

self.validate = function(form) {
return $(form).validate();
};
};

var viewModel = new ViewModel();


ko.applyBindings(viewModel);
</script>
</body>
</html>

Ejemplo 5-5 crea un formulario que recoge un primer nombre, apellido y dirección de correo
electrónico. Estos campos son todos los datos enlazados a campos en el ViewModel
correspondiente.

El formulario también contiene un enlace a los datos de la submit binding que le dice a Knockout
llamar al validar la función que se ha creado en el viewModel.

Discreta formulario de validación | 37


La validate función acepta el formulario como un parámetro de entrada. Acontinuación, llama
la validate fecha de la función de la forma que la validación discreta añade automáticamente a la
formar. Si la validación se realiza correctamente, se envía el formulario; de lo contrario, un error
hará se mostrará al usuario corregir la información.

Los tres campos de formulario que se hayan marcado required y debido a que se especifica el
campo email de tipo email, la validación de formularios asegura que tengo un email válido.

38 | Capítulo 5: Trabajo con formularios


CAPÍTULO 6
Múltiples ViewModels, datos personalizados
binding y Plantillas

Binding múltiples ViewModels


Ya sea que usted está buscando simplemente para mantener sus ViewModels agradable y limpio, o
si tener un viewModel compartido que aparece en cada página, llegará un momento en el que
deseen enlazar varios ViewModels en una sola página.

Knockout hace esto muy fácil. Hay un segundo parámetro opcional que puede ser pasado
al ko.applyBindings función que le dice a golpe de gracia para limitar su unión dentro de este
bloque de HTML.

Ejemplo 6-1 se ve casi lo mismo que algunos de los ejemplos anteriores, donde un simple nombre
estaba siendo obligado. Hay algunas diferencias sutiles que conseguir que esto funcione.

Ejemplo 6-1. La unión de dos ViewModels en una sola página

<!DOCTYPE html>
<html>
<head>
<title>Data Binding with KnockoutJS</title>
</head>
<body>

<div id="viewModel1">
<h1 data-bind="text: name"></h1>
</div>

<div id="viewModel2">
<h1 data-bind="text: name"></h1>
</div>
39
<script type='text/javascript' src='js/knockout-3.2.0.js'></script>
<script>
function ViewModel(name) {
var self = this;

self.name = name;
};

var viewModel1 = new ViewModel('Steve Kennedy');


ko.applyBindings(viewModel1, document.getElementById('viewModel1'));

var viewModel2 = new ViewModel('Mike Wilson');


ko.applyBindings(viewModel2, document.getElementById('viewModel2'));
</script>
</body>
</html>

En primer lugar, hay dos h1 etiquetas colocadas dentro de su propio div etiqueta. Esta div etiqueta
contiene una Identificación del atributo que es importante para que este ejemplo funcione
correctamente.

En el JavaScript, el mismo modelo de vista se crea una instancia dos veces, cada uno con una
diferente nombre que la de entrada. Esta variable se hace pasar a continuación a
las ko.applyBindings como normal; sin embargo, esta vez un segundo parámetro se pasa
también. El div elemento se pasa por lo Knockout solamente se unirá dentro de ese elemento.

Puede confirmar esto copiando una de las h1 etiquetas y colocarlo fuera del div . Sin texto se
mostrará a causa del golpe de gracia no realiza ninguna enlaces de datos.

La unión a un editor WYSIWYG


Nocaut viene con muchas ataduras, incorporados; Sin embargo, hay ocasiones en las que no llegan
a resolver el problema en cuestión. Por suerte, golpe de gracia le permite construir una costumbre
vinculante que puede estar unido a cualquier propiedad HTML.

Un ejemplo perfecto de esto es un editor WYSIWYG como el muy popular (y gratuito)

TinyMCE. TinyMCE convierte cualquier ordinaria textarea en un editor HTML sofisticado. Por
desgracia, TinyMCE en realidad no cambia el contenido del textarea a que se aplican,
el value vinculante (binding).

En el ejemplo 6-2 , textarea será atado a una costumbre Knockout unión llamada tinymce. Cuando
el usuario trabaja con el editor, una vista previa en vivo está vinculado a un div etiqueta usando
el html vinculante. Un botón también está ligado a una función que se restablecerá las contenidas
del editor para nada.

40 | Capítulo 6: Múltiples ViewModels, enlaces de datos personalizados y plantillas


Para empezar, visite la página de descarga TinyMCE y obtener la versión que se construye con el
paquete de jQuery. Este libro utiliza la versión 4.1.5.

Ejemplo 6-2. área de texto con destino a TinyMCE

<!DOCTYPE html>
<html><head>
<title>Data Binding with KnockoutJS</title>
</head>
<body>
<form>
<textarea data-bind="tinymce: htmlText"></textarea>
</form>

<button type="button" data-bind="click: resetContent">Reset content</button>

<h2>Preview</h2>
<div data-bind="html: htmlText"></div>

<script type='text/javascript' src='js/jquery.js'></script>


<script type='text/javascript' src='js/tinymce/jquery.tinymce.min.js'></script>
<script type='text/javascript' src='js/tinymce/tinymce.min.js'></script>
<script type='text/javascript' src='js/knockout-3.2.0.js'></script>
<script type='text/javascript' src='js/kobinding.js'></script> <script>
function ViewModel() {
var self = this;

self.htmlText = ko.observable();

self.resetContent = function() {
self.htmlText('');
};
};

var viewModel = new ViewModel();


ko.applyBindings(viewModel);
</script>
</body>
</html>

Hasta ahora, esto se ve idéntica a muchos ejemplos anteriores muestran. textarea está obligado a
una propiedad llamada htmlText . El botón está ligada a una función llamada resetContent, que
simplemente se establece el htmlText propiedad a una cadena vacía. La vista previa es un div que
se dirigía a los datos a través de la html unión al mismo htmlText propiedad.

Varios guiones se incluyen: jQuery, TinyMCE, y el golpe de gracia de nueva creación


Unión. Un nuevo archivo llamado kobinding.js deben ser creados y colocados dentro de la
previamente creado js carpeta. El contenido de este archivo se muestra en la Ejemplo 6-3 .

La unión a un editor WYSIWYG | 41


Ejemplo 6-3. La unión tinymce, kobinding.js

ko.bindingHandlers.tinymce = {
init: function (element, valueAccessor, allBindingsAccessor) {
var tinymceOptions = { setup: function (editor) {
editor.on('change', function (event) {
valueAccessor()(event.target.getContent());
});
}
};

$( element ).text( valueAccessor()() );

setTimeout( function() {
$( element ).tinymce(tinymceOptions);
}, 0 );

ko.utils['domNodeDisposal'].addDisposeCallback( element, function() {


$( element ).tinymce().remove();
});
},
'update': function( element, valueAccessor, allBindings ) {
var tinymce = $( element ).tinymce(), value = valueAccessor()();

if ( tinymce ) {
if ( tinymce.getContent() !== value ) {
tinymce.setContent( value );
}
}
}
};

Para empezar, se dará cuenta de que dos propiedades se definen para la tinymce vinculante: init
y update.

El init propiedad se denomina en primera inicialización (es decir, cuando ko.applyBindings es


primera llamada). Esto hace un par de cosas. En él se establecen las opciones TinyMCE y se une al
textarea a TinyMCE.

En la configuración de TinyMCE, creé una función de configuración en línea que detecta el cambio
evento que se trigged por TinyMCE. Cada vez TinyMCE dispara el change event, el observable
que se une a la unión de encargo se actualiza con el contenido del editor TinyMCE.

La update propiedad se activa cada vez que los cambios observables a través de código (es decir, en
el ejemplo anterior, a partir clic en el botón de reinicio). Dentro de esta función, se obtiene el editor
TinyMCE que el observables está obligado a y establece el contenido de TinyMCE a el valor actual
de lo observable.

42 | Capítulo 6: Múltiples ViewModels, enlaces de datos personalizados y plantillas


valueAccessor () ()
Si se preguntan por qué se están utilizando dos conjuntos de paréntesis, la propiedad de
entrada valueAccessor es una función de JavaScript, que (Cuando se ejecuta) proporciona
acceso a la propiedad actual modelo valor. Este es el primer conjunto de paréntesis.

Se requiere que el segundo conjunto de paréntesis porque el model propiedades es una


propiedad observada, lo que significa que es una función y necesita ser ejecutado para
acceder al valor actual.

La unión a una plantilla Knockout


Organización del código y la reutilización de código son factores importantes en casi cualquier
tamaño de proyecto. Knockout ayuda con la organización o reutilizar sus plantillas HTML
mediante la creación de una plantilla de Knockout.

Incluso si usted no utiliza una plantilla, la funcionalidad subyacente convierte el golpe de gracia
Estructura HTML en foreach, with, y if fijaciones en una sola.

En el Ejemplo 6-4 , Voy a actualizar un ejemplo del Capítulo 3 que da salida a una lista
de libros en una mesa. El código HTML que se repite dentro del foreach vinculante será
se extrajo en una plantilla, lo que permite una fácil reutilización.

Ejemplo 6-4. Uso de una plantilla con la unión de un foreach

<!DOCTYPE html>
<html>
<head>
<title>Data Binding with KnockoutJS</title>
</head>
<body>
<table>
<thead>
<tr>
<th>Title</th>
<th>ISBN</th>
<th>Published</th>
</tr>
</thead>
<tbody data-bind="template: { name: 'book-template', foreach: books }">
</tbody>
</table>

<script type='text/javascript' src='js/knockout-3.2.0.js'></script>

<script type="text/html" id="book-template">


<tr>
<td data-bind="text: title"></td>

la unión a una plantilla knockout | 43


<td data-bind="text: isbn"></td>
<td data-bind="text: $parent.formatDate(publishedDate)"></td>
</tr>
</script>

<script>
function ViewModel() {
var self = this;

self.books = [
{
title: 'Rapid Application Development With CakePHP', isbn:
'1460954394',
publishedDate: '2011-02-17'
}, {
title: '20 Recipes for Programming MVC 3: Faster,
Smarter Web Development',
isbn: '1449309860', publishedDate: '2011-10-
14'
}, {
title: '20 Recipes for Programming PhoneGap:
Cross-Platform Mobile Development for Android and iPhone', isbn:
'1449319548',
publishedDate: '2012-04-06'
}
];

self.formatDate = function(dateToFormat) {
var months = new Array("January", "February", "March", "April", "May",
"June", "July", "August", "September", "October", "November", "December");

var d = new Date(dateToFormat);

return months[d.getMonth()] + ' ' + d.getDate() + ', ' +


d.getFullYear();
};
};

var viewModel = new ViewModel();


ko.applyBindings(viewModel);
</script>
</body>
</html>

Este ejemplo es casi idéntico al ejemplo anterior con dos modificaciones principales.

En primer lugar, el foreach unión está envuelto dentro de una template de unión. la plantilla
encuadernación Indica Knockout para llamar al book-template para cada elemento en el foreach
Unión (binding).

44 | Capítulo 6: Múltiples ViewModels, enlaces de datos personalizados y plantillas


En segundo lugar, el código HTML que estaba previamente en el foreach vinculante ha sido
movido dentro de un script de la etiqueta que contiene un tipo diferente escritura de text/html , y el
id del atributo se establece en el book-template .

Las plantillas proporcionan una excelente manera de mejorar la organización y la reutilización de


bloques de código HTML. Cuanto mayor sea su proyecto, más importante que esto se convertirá!

La unión a una plantilla Knockout | 45


CAPÍTULO 7
Mejorar las observables

Extendiéndose observables
Observables por sí solas pueden lograr un poco. Por supuesto, hay momentos con cada marco que
simplemente no puede hacer todo lo que desee. knockout ofrece la capacidad de escribir funciones
personalizadas para extender cualquier observable.

Ejemplo 7-1 se extenderá (juego de palabras) del ejemplo anterior que contaba caracteres para
evitar la entrada de texto de entrar en más de la cantidad asignada.

Ejemplo 7-1. La extensión de un observable

<!DOCTYPE html>
<html>
<head>
<title>Data Binding with KnockoutJS</title>
</head>
<body>

<textarea data-bind="textInput: myText"></textarea><br/>


Characters remaining: <span data-bind="text: charactersRemaining"></span>

<script type='text/javascript' src='js/knockout-3.2.0.js'></script>


<script type='text/javascript' src='js/maxCharacters.js'></script>
<script>
function ViewModel() {
var self = this;

self.maxCharacters = 140;

self.myText = ko.observable('')
.extend({maxCharacters: self.maxCharacters});

47
self.charactersRemaining = ko.computed(function() {

return self.maxCharacters - self.myText().length;


});

};

var viewModel = new ViewModel();

ko.applyBindings(viewModel);
</script>

</body>

</html>

En el ejemplo anterior, el myText observables se ha extendido a llamar a la maxCharteres función, pasando


de una variable que contiene los caracteres como máximo para permitir (ver Ejemplo 7-2 ).

El maxCharacters función se ha colocado dentro de un archivo llamado maxCharacters.js y


colocado dentro de la creada anteriormente js carpeta. Este archivo se incluye inmediatamente
después framwork de knockout.

Ejemplo 7-2. Número máximo de caracteres función se extienden

ko.extenders.maxCharacters = function(target, max) {


var result = ko.pureComputed({
read: target,
write: function(newValue) {
var current = target();

// only write the newValue if it less or equal to the


// max characters allowed
if (newValue.length <= max)
target(newValue);
// set it back to the previous value
else { target(current);
target.notifySubscribers(current);
}
}
}).extend({ notify: 'always' });

return result;
};

La función de extensión es similar a los observables calculados en que se trata de tanto la lectura y
la write de la observable que se está extendiendo.

Cada vez que el observable está a punto de cambiar, Knockout llama a


la write función. Estafunción comprueba si el nuevo valor es menor que el máximo permitido
caracteres. Si lo es, escribe el valor en el observable.

48 | Capítulo 7: Mejora observables


Si no es así, se restablece la parte posterior observable al valor anterior. Una vez hecho esto, se dice
observable para notificar a todos los abonados que el valor ha cambiado. Esto es requerido porque
técnicamente el valor no ha cambiado, pero diciendo a los suscriptores que tiene, causa del golpe
de gracia para actualizar el contenido del cuadro de texto y quitar el recién añadido caracteres
cuando el valor excede la longitud máxima.

Adición de funciones personalizadas a Observables


Al igual que en los observables se extienden, también puede adjuntar una función personalizada
para ellos. Knockout utiliza para este tipo de herencia observable, por lo que donde se conecta el
funcionamiento determinará qué tipos de observables tienen acceso a ella.

En la parte superior de la jerarquía es el suscribable clase; todos los tipos de observables heredan
de esta clase. Si desea agregar una función personalizada que está disponible para cualquier
observación Tipo ble, se uniría a esta clase.

De suscribable, la observable propiedad y computed rama observables fuera.


El observableArray tipo amplía aún más la observable propiedad. Esto significa que si se adjunta
una función a la calculada , sólo estará disponible para ese tipo. Sin embargo, si lo añade
a observables , sino que también estará disponible para el observableArray .

En el ejemplo 7-3 , los libros están de vuelta. Esta vez, una lista de libros se mostrará en una lista
desordenada. Cada libro contiene una casilla de verificación que está enlazado a datos a la owned
propiedad observable del libro.

A continuación se añade una función al observableArray llamada booksOwned que creará una
nueva lista de los principales books de matriz que contiene solamente los libros donde la
propiedad owned es true.

a continuación, se muestra esta lista en una lista desordenada separada que muestra todos los libros
tu posees.

Ejemplo 7-3. Adición de una función a un observableArray

<!DOCTYPE html>
<html>
<head>
<title>Data Binding with KnockoutJS</title>
</head>
<body>

List of books
<ul>
<!-- ko foreach: books -->
<li>
<input type="checkbox" data-bind="attr: { id: isbn }, checked: owned "/>
<label data-bind="attr: { for: isbn }, text: title"></label>

La adición de funciones personalizadas a observables | 49


</li>
<!-- /ko -->
</ul>

Books you own


<ul>
<!-- ko foreach: booksOwned -->
<li data-bind="text: title"></li>
<!-- /ko -->
</ul>

<script type='text/javascript' src='js/knockout-3.2.0.js'></script>


<script type='text/javascript' src='js/booksOwned.js'></script>
<script>
function ViewModel() {
var self = this;

self.books = ko.observableArray([
{
title: 'Rapid Application Development With CakePHP', isbn:
'1460954394',
owned: ko.observable(false)
}, {
title: '20 Recipes for Programming MVC 3: Faster,
Smarter Web Development',
isbn: '1449309860',
owned: ko.observable(false)
}, {
title: '20 Recipes for Programming PhoneGap:
Cross-Platform Mobile Development for Android and iPhone', isbn:
'1449319548',
owned: ko.observable(false)
}
]);

self.booksOwned = self.books.booksOwned('owned', true);


};

var viewModel = new ViewModel();


ko.applyBindings(viewModel);
</script>
</body>
</html>

Cuando se selecciona la casilla de verificación, se establecerá la propiedad owned en true. Dentro


de ViewModel, se define otra llamada booksOwned (ver Ejemplo 7-4 ). Este es un subconjunto de
array de libros que llama a la booksOwned función personalizada que se ha agregado a
la observableArray. Esta función acepta dos entradas, la propiedad que se utilizará en la
comparación y el valor que se debe incluir en el nuevo lista.

50 | Capítulo 7: Mejora observable


El ejemplo anterior incluye un nuevo guión llamado booksOwned.js que se coloca dentro de la
creada anteriormente js carpeta.

Ejemplo 7-4. función personalizada booksOwned

ko.observableArray.fn.booksOwned = function(property, value) {


return ko.pureComputed(function() {
var allItems = this();
var matchingItems = [];

for (var i = 0; i < allItems.length; i++) {


var current = allItems[i];

if (ko.unwrap(current[property]) === value)


matchingItems.push(current);
}

return matchingItems;
}, this);
};

Esta función crea dos matrices: allItems y matchingItems. allItems contiene la matriz original. Esta
matriz es entonces enrollada a través, y el uso de las entradas a esta función, el elemento de la
matriz se compara con el valor esperado. Si hay un partido, que se añade a la segunda
matriz, matchingItems. Entonces se devuelve esta matriz.

Debido a que esta función se envuelve dentro de una función computarizada, que se ha configurado
para suscribirse a cualquier cambio en el observableArray o propiedades dentro de esa matriz que
son se observa. Así que cada vez que uno de los cambios, se ejecuta esta función, y la lista de libros
propiedad se actualiza.

Limitante de la velocidad observables


Es posible que haya un escenario en el que desea retrasar la respuesta a un observable que ha
cambiado. Knockout contiene una función ampliada incorporado que le permite limitar ¿con qué
frecuencia se notifica a los suscriptores a lo observable.

Porque puede haber varias maneras diferentes que desea limitar, Knockout tiene previsto a algunos
métodos diferentes.

En su forma más simple, como se muestra en el Ejemplo 7-5 , Se puede ampliar el observable y
establecer un valor de tiempo de espera (en milisegundos) que debe esperar Knockout.

Ejemplo 7-5. actualizaciones de retardo en un segundo

myObservable.extend({ rateLimit: 1000 });


Limitante de la velocidad observables | 51
En este ejemplo también se puede extender (sin doble sentido en esta ocasión) a no limitarse a
esperar la dado milisegundos, pero en lugar de esperar dadas milisegundos después de los cambios
se han detenido. Ver Ejemplo 7-6 .

Ejemplo 7-6. Actualizar un segundo después de los cambios se han detenido

myObservable.extend( {
rateLimit: {
timeout: 1000,
method: "notifyWhenChangesStop"
}
});

En el Ejemplo 7-7 , Pensé que iba a estar ordenado para crear nuestra propia
autocompletar. Mediante el uso la Ratelimit cuando cambia el valor de retardo, una lista de los
lenguajes de programación se ser buscada. Cualquier coincidencia se añadirá a
un observableArray y se muestran en una lista desordenada.

Ejemplo 7-7. Autocompletar usando Knockout

<!DOCTYPE html>
<html>
<head>
<title>Data Binding with KnockoutJS</title>
</head>
<body>

<label for="tags">Filter a tag: </label>


<input id="tags" data-bind="textInput: tag"><br/><br/>

Tags Matching:
<ul>
<!-- ko foreach: matchedTags -->
<li data-bind="text: $data"></li>
<!-- /ko -->
</ul>

<script type='text/javascript' src='js/knockout-3.2.0.js'></script>


<script>
function ViewModel() {
var self = this;

self.availableTags = [
"ActionScript", "AppleScript", "Asp", "BASIC", "C",
"C++", "Clojure", "COBOL", "ColdFusion", "Erlang",
"Fortran", "Groovy", "Haskell", "Java", "JavaScript",
"Lisp",
"Perl", "PHP", "Python", "Ruby", "Scala",
"Scheme"

52 | capítulo 7: mejora observables


];

self.matchedTags = ko.observableArray([]);

self.tag = ko.observable().extend({
rateLimit: {
timeout: 1000,
method: "notifyWhenChangesStop"
}
});

self.tag.subscribe(function(value) {
self.matchedTags.removeAll();

if (value !== '') {


for (var i = 0; i < self.availableTags.length; i++) {
if (self.availableTags[i].toLowerCase().indexOf(value) >= 0)
self.matchedTags.push(self.availableTags[i]);
}
}
});
};

var viewModel = new ViewModel();


ko.applyBindings(viewModel);
</script>
</body>
</html>

En el ejemplo anterior, se crean dos matrices. Una de ellas contiene la lista de etiquetas de
buscar. El otro es un observableArray que será actualizado y contiene etiquetas que coincidir con
lo que el usuario escribe.

Una tags observable también se crea y se extiende con un Ratelimit de un segundo después de que
deja de cambiar. Esta propiedad está suscrita a continuación. Cuando cambia la propieda (después
del retardo), esta función se llama. Despeja las matchedTags matriz y bucles a través de la lista de
etiquetas y añade ningún partido a la matriz.

Si ejecuta este ejemplo y escribe una en el cuadro de texto que está obligado-datos al retraso
propiedad observada, después de un segundo de la lista de etiquetas coincidentes se rellenará con
varios partidos. No es tan bonito como autocompletar de jQuery, pero me gusta el mejor limitación
de velocidad que ¡proporciona!

Limitante de la velocidad observables | 53


CAPÍTULO 8
La interacción del lado del servidor

Knockout no incluye ninguna función para llevar a cabo la interacción del lado del servidor. Es
completamente de usted. Puede usar jQuery, vainilla JavaScript o cualquier otra biblioteca que tu
prefiere.

Sugiero usar jQuery porque algunos de los ejemplos anteriores ya se están utilizando, que significa
que es muy probable que ya incluye en su proyecto, lo que añade sin adiciones override cional.

Envío de datos

Cuando se trata de enviar datos, que suelen utilizar unos formatos diferentes dependiendo de si
estoy interactuando con una API REST o si estoy POSTeando un formulario estándar. Ahí dos
grandes diferencias cuando se cambia entre los tipos:

1. El content-type de la AJAX petición es diferente. La forma predeterminada cuando


presentando una petición AJAX usando jQuery es application / x-www-form-urlencoded.
Esto se usaría para un puesto de forma estándar. Un típico content-type cuando trabajar con
una API REST sería application / json cuando se trabaja en JSON .

2. El contenido de la forma es también diferente. Cuando se realiza una forma estándar POST,
los datos se serializado como un par JavaScript clave / valor estándar de datos.
Considerando que, al interactuar con un API REST JSON, los datos del formulario serían
SERIAS alized a JSON. Knockout proporciona funciones útiles para desenvolver
observables y convertirlos a los tipos necesarios que serán exploradas en la próxima
Ejemplos.

En el Ejemplo 8-1 , voy a crear una forma estándar que aprovecha varios Knockout
binding. Cuando se envía el formulario, que se llevará a cabo a través de AJAX con el modelo
normalizado ENVIAR.
55
Ejemplo 8-1. POST de formulario a través de AJAX

<!DOCTYPE html>
<html>
<head>
<title>Data Binding with KnockoutJS</title>
</head>
<body>
<form data-bind="submit: validateAndSave, with: person">
<input type="text" data-bind="value: firstName"
placeholder="Enter your first name" required /><br/>
<input type="text" data-bind="value: lastName" placeholder="Enter
your last name" required /><br/>
<input type="email" data-bind="value: email"
placeholder="Enter your email" required /><br/>
<input type="submit" />
</form>

<script type='text/javascript' src='js/jquery.js'></script>


<script type='text/javascript' src='js/jquery.validate.min.js'></script>
<script type='text/javascript' src='js/knockout-3.2.0.js'></script>
<script>
function ViewModel() {
var self = this;

self.person = {
firstName: ko.observable(), lastName:
ko.observable(), email: ko.observable()
};

self.validateAndSave = function(form) {
if (!$(form).validate()) return;

$.ajax({
url: 'myform.aspx',
data: ko.toJS(self.person), type: 'POST',
contentType: 'application/x-www-form-urlencoded'
}).success(self.successSave).error(self.errorSave);
};

self.successSave = function() {
alert('Success!');
};

self.errorSave = function() {
alert('Error!');
};
};

var viewModel = new ViewModel();

56 | capítulo 8: interacción del lado del servidor


ko.applyBindings(viewModel);
</script>
</body>
</html>

Este ejemplo es bastante similar al ejemplo mostrado en el Capítulo 5 . Unos pocos cambios clave
ha sido hecho. En primer lugar, los elementos observables anteriores para los campos han sido
convierte en un objeto person . Esto se hace para que sea más fácil de someter la totalidad del
objeto a través de AJAX. Esto se puede ver el interior de la validateAndSave función donde los
datos siendo suministrado a través de AJAX se Uso de la función Knockout ko.toJS para convertir
los person de un objeto en JavaScript par clave / valor utilizable.

Si la llamada AJAX tiene éxito, el successSave se ejecutará la función, que se mostrar un mensaje
de éxito en un diálogo de alerta. Del mismo modo, si se produce un error, el error Guardar función
será llamada, y se muestra un mensaje de error.

Ejecutar el Ejemplo

El resultado más probable de ejecutar el ejemplo anterior hará que el diálogo de error que aparezca
ya que la forma está intentando enviar a una página llamada myform.aspx, que no existe.

Los únicos otros cambios significativos en el ejemplo anterior son los form datos de la etiqueta
Enlaces. Anteriormente, se une a un Validar función; ahora se une a una validateAndSave función
(un nombre un poco más detallado). También aprovecha la with binding a la objeto person de
evitar prefijar cada elemento de entrada a ese objeto.

Ejemplo 8-1 es la presentación de un formulario de envío estándar a través de AJAX como se


describió anteriormente. A convertir este ejemplo para enviar el formulario a través de JSON, dos
cambios menores tienen que ser hecha a la llamada AJAX dentro de
la validateAndSave función. Ver Ejemplo 8-2 .

Ejemplo 8-2. después formulario a través de JSON

$.ajax({
url: 'myform.aspx',
data: ko.toJSON(self.person), type: 'POST',
contentType: 'application/json'
})

En lugar de utilizar el ko.toJS función, que es sustituido por el muy similar ko.toJSON
función. Como su nombre lo indica la función, esto convierte la persona objeto en un JSON
representación de la misma. El otro cambio es menor que contentType se establece ahora
en application / json.

Envío de datos | 57
ko.toJS y ko.toJSON

Los ko.toJS y ko.toJSON funciones de forma recursiva pasan por el todo objeto y desenvolver
propiedades o matrices de ser una propiedad observada a una propiedad común de JavaScript.

Los datos que recibe

Recepción de datos es bastante similar al envío de datos, pero a la inversa. Cuando va a enviar
datos a un servidor, que suelen utilizar los observables para capturar y enviar datos. cuando
recibiendo ción de datos, es muy habitual el uso de propiedades o características observables para
almacenar la respuesta de un servidor.

Para demostrar esto, Ejemplo 8-3 convertirá el ejemplo autocompletar creado en Capítulo 7 . Esta
vez, el availableTags matriz no se incluirá directamente en el modelo de vista; los datos serán
cargados a través del servidor.

Ejemplo 8-3. Del lado del servidor automático de datos completa

<!DOCTYPE html>
<html>
<head>
<title>Data Binding with KnockoutJS</title>
</head>
<body>

<label for="tags">Filter a tag: </label>


<input id="tags" data-bind="textInput: tag"><br/><br/>

Tags Matching:
<ul>
<!-- ko foreach: matchedTags -->
<li data-bind="text: $data"></li>
<!-- /ko -->
</ul>

<script type='text/javascript' src='js/jquery.js'></script>


<script type='text/javascript' src='js/knockout-3.2.0.js'></script>
<script>
function ViewModel() {
var self = this;

self.matchedTags = ko.observableArray([]);
self.availableTags = [];
self.tag = ko.observable().extend({
rateLimit: {
timeout: 1000,
method: "notifyWhenChangesStop"
}

58 | capítulo 8: interacción del lado del servidor


});

self.tag.subscribe(function(value) {
self.matchedTags.removeAll();

if (value !== '') {


for (var i = 0; i < self.availableTags.length; i++) {
if (self.availableTags[i].toLowerCase().indexOf(value) >= 0)
self.matchedTags.push(self.availableTags[i]);
}
}
});

$.ajax({
url: 'tags.html', type: 'GET',
contentType: 'application/json', dataType:
'json'
}).success(function(data) {
self.availableTags = data;
});
};

var viewModel = new ViewModel();


ko.applyBindings(viewModel);
</script>
</body>
</html>

Este ejemplo es casi idéntica a la del ejemplo anterior con la excepción de que la availableTags se
rellena con la respuesta de la llamada AJAX recién agregado.

La solicitud llamadas AJAX tags.html (mostrado en el Ejemplo 8-4 ), Que contiene la misma lista
de etiquetas del ejemplo anterior. Debido a que la llamada AJAX se le dijo la respuesta sería JSON,
la matriz de palabras se puede almacenar directamente en el availableTags matriz.

Ejemplo 8-4. Tags.html

[
"ActionScript", "AppleScript", "Asp", "BASIC", "C",
"C++", "Clojure", "COBOL", "ColdFusion", "Erlang",
"Fortran", "Groovy", "Haskell", "Java", "JavaScript",
"Lisp",
"Perl", "PHP", "Python", "Ruby", "Scala",
"Scheme"
]

Si el servidor contiene un lenguaje que tiene acceso a la base de datos, esto sería una perfecta
ejemplo para implementar ir a buscar la lista de etiquetas de un archivo no estático.

Recepción de datos | 59
Pueden requerir a un servidor Web

El ejemplo anterior podría requerir un servidor web para ejecutar. Si tu fueron previamente en
marcha los ejemplos como archivos HTML (por ejemplo, la dirección URL era file: // ), es
posible que reciba un error al realizar la AJAX solicitud.

Para fines de desarrollo, recomiendo la creación de XAMPP servidor , que es compatible con
Windows, Linux y Mac OS X. Una vez instalado, los archivos de ejemplo se pueden colocar
dentro de la htdocs directorio donde está instalado XAMPP.

En el Ejemplo 8-4 , Se carga la lista de etiquetas disponibles vez, almacenado localmente, y


buscado cada vez. A menudo, al realizar este tipo de operación, que podría estar en una la cantidad
de datos más grande, establecido en bucle a través de una matriz no es práctico.

Un ejemplo de este tipo podría ser alterado fácilmente para mover el juego de etiquetas para la
servidor y tomar los resultados y el establecimiento de la matchedTags gama observable desde el
respuesta.

Ejemplo 8-5 demostrará parcialmente este pretendiendo la respuesta a la AJAX solicitud contiene
sólo las etiquetas que responden a nuestra consulta.

Ejemplo 8-5. Autocompletar que actualiza los matchedTags

<!DOCTYPE html>
<html>
<head>
<title>Data Binding with KnockoutJS</title>
</head>
<body>

<label for="tags">Filter a tag: </label>


<input id="tags" data-bind="textInput: tag"><br/><br/>

Tags Matching:
<ul>
<!-- ko foreach: matchedTags -->
<li data-bind="text: $data"></li>
<!-- /ko -->
</ul>

<script type='text/javascript' src='js/jquery.js'></script>


<script type='text/javascript' src='js/knockout-3.2.0.js'></script>
<script>
function ViewModel() {
var self = this;

self.matchedTags = ko.observableArray([]);
self.tag = ko.observable().extend({

60 | Capítulo 8: Interacción del lado del servidor


rateLimit: {
timeout: 1000,
method: "notifyWhenChangesStop"
}
});

self.tag.subscribe(function(value){
self.matchedTags.removeAll();
if (value !== '') {
$.ajax({
url: 'tags.html',
type: 'GET',
contentType: 'application/json',
dataType: 'json'
}).success(function(data) {
self.matchedTags(data);
});
}
});
};

var viewModel = new ViewModel();


ko.applyBindings(viewModel);
</script>
</body>
</html>

El ejemplo anterior ya no contiene los availableTags y el bucle para realizar el partido; en cambio,
cualesquiera que sean los resultados son de la petición AJAX son directamente asignado a
la matchedTags matriz observable.

El uso de nuestra imaginación por un minuto, cuando se escribe en el cuadro de etiquetas, si


el tags.html página se han de realizar la lógica de filtrado, los matchedTags se filtran
correctamente cada vez que se ha escrito sobre la base de la respuesta dinámica del servidor.

Recepción de datos | 61
CAPÍTULO 9
El Mapeo Plugin

Knockout permite a los desarrolladores ampliar el marco de la creación de plugins, y el


desarrollador original ha creado uno llamado mapeo . El propósito de este plugin es asignar
automáticamente un objeto de JavaScript estándar o datos JSON a un nuevo objeto con cada
propiedad de ser un objeto observable.

Esto puede ser extremadamente útil en varias situaciones diferentes. Donde habíamos estado
creando objetos y definir manualmente cada propiedad como observables, el plug-in de mapeo lo
hará automáticamente.

Para empezar, descarga el archivo JavaScript adicional . Me salvó la mía en el vigente


js carpeta y lo llamó knockout.mapping.js , y así es como estos próximos ejemplos hacer referencia
al archivo.

Mapa de un objeto
En el Ejemplo 9-1 , convierto un ejemplo de Capítulo 3 que demuestra la con la unión en un
solo libro objeto. En el ejemplo original, un book objeto se pasa a la viewModel a través del
constructor. Esto entonces se asigna directamente al interior del libro propiedad y se utiliza para
mostrar con fijaciones Knockout. Sin embargo, debido a que es utilizando el plugin mapeo, todas
las propiedades (title , synopsis , y publishedDate ) que se están observando ahora.

Ejemplo 9-1. Mapeo de un objeto

<!DOCTYPE html>
<html>
<head>
<title>Data Binding with KnockoutJS</title>
</head>

63
<body>

<div id="book" data-bind="with: book">


<h1 data-bind="text: title"></h1>
<h2>Published on <span data-bind="text: $parent.formatDate(publishedDate())">
</span></h2>
<p data-bind="text: synopsis"></p>
</div>

<script type='text/javascript' src='js/knockout-3.2.0.js'></script>


<script type='text/javascript' src='js/knockout.mapping.js'></script>
<script>
function ViewModel(book) {
var self = this;

self.book = ko.mapping.fromJS(book);

self.formatDate = function(dateToFormat) {
var months = new Array("January", "February", "March", "April", "May",
"June", "July", "August", "September", "October", "November", "December");

var d = new Date(dateToFormat);

return months[d.getMonth()] + ' ' + d.getDate() + ', ' +


d.getFullYear();
};
};

var book = {
title: 'Rapid Application Development With CakePHP', synopsis:
'...',
isbn: '1460954394', publishedDate: '2011-02-
17'
};

var viewModel = new ViewModel(book);


ko.applyBindings(viewModel);
</script>
</body>
</html>

En el ejemplo original, cuando se muestra la fecha de publicación a través de una función, la


propiedad no estaba siendo observada; en este ejemplo, es, es decir, cuando esta función está
llamada, el publishedDate requiere paréntesis para ejecutar la función y el retorno el valor
observado.

El otro cambio es la propiedad reserva ahora se está estableciendo con los ko.mapping.fromJS
función, que convierte cada propiedad en un observable.

64 | Capítulo 9: El Mapeo Plugin


Una función de este tipo puede ser extremadamente útil cuando la creación de una forma que es
prepoblado de un objeto de datos de gran tamaño, lo que evita la necesidad de mapear cada
propiedad manualmente.

Mapa de JSON (o un servidor)


En la sección anterior, los datos se están asignando a partir de un objeto JavaScript. Es también
bastante normal que desee asignar los datos de JSON a través de una petición AJAX. Ejemplo 9-2
también se extenderá un ejemplo de Capítulo 3 . Una lista de libros se mostrará en una mesa. El
título del libro se ha convertido en un enlace. Cuando el usuario selecciona la libro, una petición
AJAX se hace para obtener los detalles del libro. Usando el plugin mapeo, la respuesta está
asignada a una propiedad de contabilidad interna y se utiliza para mostrar dinámicamente el libro.

Ejemplo 9-2. Mapeo de JSON

<!DOCTYPE html>
<html>
<head>
<title>Data Binding with KnockoutJS</title>
</head>
<body>

<!-- ko if: hasFetchedBook -->


<div id="book" data-bind="with: book">
<img data-bind="attr: { src: image }" />
<h1 data-bind="text: title"></h1>
<h2>Published on
<span data-bind="text: $parent.formatDate(publishedDate())">
</span></h2>
<p data-bind="text: synopsis"></p>
</div>
<!-- /ko -->
<table>
<thead>
<tr>
<th>Title</th>
<th>ISBN</th>
<th>Published</th>
</tr>
</thead>
<tbody data-bind="foreach: books">
<tr>
<td><a href="#" data-bind="text: title, click: $parent.loadBook">
</a></td>
<td data-bind="text: isbn"></td>
<td data-bind="text: $parent.formatDate(publishedDate)"></td>
</tr>

Mapa de JSON (o un servidor) | 65


</tbody>

</table>

<script type='text/javascript' src='js/jquery.js'></script>


<script type='text/javascript' src='js/knockout-3.2.0.js'></script>
<script type='text/javascript' src='js/knockout.mapping.js'></script>
<script>
function ViewModel() {
var self = this;

self.books = [
{
title: 'Rapid Application Development With CakePHP', isbn:
'1460954394',
publishedDate: '2011-02-17'
}, {
title: '20 Recipes for Programming MVC 3: Faster,
Smarter Web Development',
isbn: '1449309860', publishedDate: '2011-10-
14'
}, {
title: '20 Recipes for Programming PhoneGap:
Cross-Platform Mobile Development for Android and iPhone', isbn:
'1449319548',
publishedDate: '2012-04-06'
}
];

self.book;
self.hasFetchedBook = ko.observable(false);

self.loadBook = function() {
$.ajax({
url: 'book.html', type: 'GET',
contentType: 'application/json'
}).success(function(data) {
self.book = ko.mapping.fromJSON(data, self);
self.hasFetchedBook(true);
});
};

self.formatDate = function(dateToFormat) {
var months = new Array("January", "February", "March", "April", "May",
"June", "July", "August", "September", "October", "November", "December");

var d = new Date(dateToFormat);

return months[d.getMonth()] + ' ' + d.getDate() + ', ' +


d.getFullYear(); };
};

66 | Capítulo 9: El Mapeo Plugin

var viewModel = new ViewModel();


ko.applyBindings(viewModel);
</script>
</body>
</html>

En este ejemplo, el título del libro dentro de la tabla es un enlace HTML que se está enlazado a
datos a la loadBook función de clic. Esta función realiza una AJAX solicitud a la book.html página
(que se muestra en el Ejemplo 9-3 ). Esta contiene datos similares a el libro codificado en el
ejemplo anterior. Esta función también se establece la observables hasFetchedBook a la
verdadera causa del HTML para rendir de forma dinámica y mostrar el libro, imagen y todos.

En el ejemplo anterior, la propiedad libro se está asignando a través de un objeto de JavaScript.


En el Ejemplo 9-2 , es de JSON y por lo tanto utiliza el ko.mapping.fromJSON función en lugar.

Ejemplo 9-3. book.html

{
"title": "Rapid Application Development With CakePHP",
"synopsis": "...",
"isbn": "1460954394",
"publishedDate": "2011-02-17",
"image": "http://ecx.images-amazon.com/images/I/41JC54HEroL._AA160_.jpg"
}

Lo anterior es el objeto libro anterior se transformó en JSON válida para ser analizada por el Plugin
de mapeo.

El mismo libro Every Time


Independientemente de qué libro se selecciona, el mismo libro será vuelto cada vez en este ejemplo ya
que no existen en el servidor lógicos capacidades para determinar dinámicamente qué libro era
seleccionado. Por supuesto, este ejemplo no será difícil de extender, por ejemplo, proporcionar el
número de ISBN del libro al servidor.

Al igual que en el apartado anterior, otro gran uso de la cartografía de JSON sería rellenar
dinámicamente un formulario por ir a buscar los datos desde el servidor, utilizando el mapeo plugin
y, a continuación, de unión a la forma a estos elementos.

Mapa de JSON (o un servidor) | 67


Haciendo caso omiso de la observación y propiedades específicas
Cuando se introdujo por primera observables, espero que identificó la importancia de no
innecesariamente haciendo de cada propiedad en un objeto observable. En el Ejemplo 9-1 , el
Plugin de mapeo utiliza el comportamiento predeterminado y convierte todo en una observación
Ble Esto es perfecto cuando todo tiene que ser observado; Sin embargo, habrá muchas veces que
esto no es el caso y el plugin proporciona opciones para escenarios como esta.

En el Ejemplo 9-4 , Vamos a juntar varias cosas diferentes que hemos discutido, pero todavía no
totalmente implementado. Este ejemplo demuestra datos de mapeo para un formulario, haciendo
solamente las propiedades necesarias observadas y utilizando funciones condicionales para trabajar
como una forma para añadir y editar.

Ejemplo 9-4. Plugin de mapeo con opciones

<!DOCTYPE html>
<html>
<head>
<title>Data Binding with KnockoutJS</title>
</head>
<body>

<form data-bind="submit: validateAndSave, with: user">


<input type="text" data-bind="value: firstName"
placeholder="Enter your first name" required /><br/>
<input type="text" data-bind="value: lastName" placeholder="Enter
your last name" required /><br/>
<input type="email" data-bind="value: email"
placeholder="Enter your email" required /><br/>

<input type="submit" data-bind="value: (id == 0) ? 'Create' : 'Update'" />


</form>

<script type='text/javascript' src='js/jquery.js'></script>


<script type='text/javascript' src='js/jquery.validate.min.js'></script>
<script type='text/javascript' src='js/knockout-3.2.0.js'></script>
<script type='text/javascript' src='js/knockout.mapping.js'></script>
<script>
function ViewModel(user) {
var self = this;

var mapping = {
'observe': [ "firstName", "lastName", "email" ],
'ignore': [ "created" ]
};

self.user = ko.mapping.fromJS(user, mapping);

68 | Capítulo 9: El Mapeo Plugin


self.validateAndSave = function(form) {
if (!$(form).validate()) return;

if (self.user.id == 0)
alert("Creating a user");
else
alert("Updating a user");
};
};

var user = {
id: 1,
firstName: 'Mike', lastName:
'Wilson',
email: 'mike@notarealaddress.com', created: '2014-
10-12'
};

var viewModel = new ViewModel(user);


ko.applyBindings(viewModel);
</script>
</body>
</html>

Este ejemplo acepta un usuario objeto en el constructor para el viewModel. Este objeto a
continuación, se asigna a un interior de usuario propiedad que está unido a los elementos de
formulario para edición.

El usuario objeto contiene varias propiedades adicionales que pueden ser útiles, pero no
editable dentro de la forma. Para no hacer que estos objetos observados innecesariamente, varios
las opciones de asignación se definen y se pasan a la función de mapeo.

En primer lugar, una lista explícita de propiedades que requieren atención se define en el observar
opción. Estas propiedades se ajustan a los elementos que están contenidos dentro de la
forma. Segundo, el ignorar la opción no se utiliza para mapear la creada propiedad, ya que no es
requerida en la página.

Haciendo caso omiso de la observación y propiedades específicas | 69


CAPÍTULO 10
Un ejemplo práctico

Para este capítulo final, pensé que sería una buena idea para ver varias de las cosas que se han
demostrado en pequeños ejemplos a lo largo de los capítulos anteriores. ¿Qué mejor manera de
hacerlo que con un carrito de la compra?

En los ejemplos anteriores, el código HTML y JavaScript a menudo se han presentado en una
ejemplo único. Debido a que este código HTML y JavaScript es un poco más grande que los
anteriores ejemplos, se ha dividido en varios pequeños archivos JavaScript y un archivo HTML.

La construcción de una cesta de la compra


Vamos a empezar por definir los objetivos de la cesta de la compra:

• Incluye una selección de categoría para permitir la fácil filtración de productos


• Mostrar una lista de productos para una determinada categoría
• Ver el número de elementos se encuentran actualmente en el carrito de la compra
• Añadir un elemento a la cesta
• Eliminar un elemento de la cesta
• Mostrar la ejecución total de artículos en el carro
• Incluir algunas animaciones simples al añadir / eliminar de la cesta

Creo que cubre las principales características de un típico carrito de compras. Al observar estas
características, veo una necesidad inmediata de tres matrices:

1. Lista de categorías con sus elementos disponibles.


2. Lista de productos disponibles para la categoría activa.
3. Lista de artículos que el usuario está interesado en comprar.

Además de estas tres matrices, queremos hacer un seguimiento de la ejecución total actual.

Ejemplo 10-1 muestra el código HTML que cubre las particularidades del carrito de la compra.

71
Ejemplo 10-1. HTML para carrito de la compra

<!DOCTYPE html>
<html>
<head>
<title>Yet Another Shopping Cart</title>
</head>
<body>

<div style="float: left; width: 50%">

<ul>
<!-- ko foreach: categories -->
<li><a href="#" data-bind=
"click: $parent.showProducts, text: name"></a></li>
<!-- /ko -->
</ul>

<ul>
<!-- ko foreach: availableProducts -->
<li>
<!-- ko template: { name: 'item-template', data: $data } -->
<!-- /ko -->
<p><button data-bind="click: $parent.addItem">Add to cart</button></p>
</li>
<!-- /ko -->
</ul>

</div>
<div style="float: left; width: 50%">

<h1>Items in cart: <span data-bind="text: cartItems().length"></span></h1>

<ul>
<!-- ko foreach:
{ data: cartItems, afterAdd: fadeIn, beforeRemove: fadeOut } -->
<li>
<!-- ko template: { name: 'item-template', data: $data } -->
<!-- /ko -->
<p><button data-bind="click: $parent.removeItem">Remove</button>
</p>
</li>
<!-- /ko -->
</ul>

<h3>Total: $<span data-bind="text: cartTotal"></span></h3>

</div>

<script type="text/html" id="item-template">


<h2 data-bind="text: getTitle()"></h2>
<p data-bind="html: getDescription()"></p>

72 | Capítulo 10: Un ejemplo práctico

p>Price: $<span data-bind="text: price"></span></p>


</script>
<script type='text/javascript' src='js/jquery.js'></script>
<script type='text/javascript' src='js/jquery-ui.js'></script>
<script type='text/javascript' src='js/knockout-3.2.0.js'></script>
<script type='text/javascript' src='js/classes.js'></script>
<script type='text/javascript' src='js/data.js'></script>
<script type='text/javascript' src='js/viewmodel.js'></script>
<script>
var viewModel = new ViewModel(categories);
ko.applyBindings(viewModel);
</script>
</body>
</html>

El uso de CSS en línea, la salida se divide en dos lados iguales. En el lado izquierdo, una lista de
categorías se muestra en una lista desordenada. Debajo de esta lista es una segunda lista
desordenada que muestra una lista de productos disponibles después de una categoría ha sido
seleccionada. Por defecto, la availableProducts matriz observable está vacía. Esta matriz se rellena
cuando una categoría se selecciona en el showProducts función que está enlazada a datos o al clic
evento.

En el lado derecho de la pantalla, los elementos de la compra se muestran en otra lista desordenada.
Usted puede notar que este foreach es diferente a los demás porque es el uso de la
afterAdd y beforeRemove funciones de devolución de llamada.

Debido a que la salida de los productos disponibles y artículos carrito seleccionados es lo mismo,
una plantilla ha sido creada para commonize la visualización del elemento. La unica diferencia
entre ellos se encuentra el botón que aparece debajo de cada elemento. Al mostrar disponibles
productos, el botón permite al usuario añadir un artículo y cuando indicación del valor
seleccionado cesta productos, el botón permite al usuario retirar el artículo.

También en el lado derecho, se muestra el número total de productos añadidos a la cesta, como se
así como la compra coste total acumulado.

Por último, hay varios archivos JavaScript que se incluyen en la parte inferior del HTML. Una
nueva biblioteca ( jQueryUI ) Se ha agregado a realizar las animaciones sencillas cuando se añaden
o eliminan de la cesta de la compra artículos. Los archivos de otro tipo ( classes.js , data.js ,
viewmodel.js ) son los archivos divididos para centrarse mejor en cada archivo a su vez. Ver
Ejemplos 10-2 , 10-3 Y 10-4 .

Ejemplo 10-2. viewmodel.js

function ViewModel(categories) {
var self = this;

self.categories = categories;

La construcción de una cesta de la compra | 73


self.availableProducts = ko.observableArray([]);

self.cartItems = ko.observableArray([]);

self.cartTotal = ko.computed(function() {
var total = 0;
for (var i = 0; i < self.cartItems().length; i++)
total += self.cartItems()[i].price;

return total;

});

self.showProducts = function(category) {
self.availableProducts(category.items);
};

self.addItem = function(item) {
self.cartItems.push(item);
};

self.removeItem = function(item) {
self.cartItems.remove(item);
};

self.fadeIn = function(element) {
$(element).hide().fadeIn('slow');
};

self.fadeOut = function(element) {
$(element).slideUp(function() {
$(element).remove();
});
};
};

Las categorías que se pasan al constructor serán mostradas en el Ejemplo 10-3 cuando el data.js se
visualiza archivo. El array se almacena en el array de categorías interno.
Los availableProducts y cartItems, se definen como matrices observables vacías que se utilizará
para mostrar dinámicamente los productos de una categoría y lo dado artículos en la compra del
usuario, respectivamente.

El cartTotal es un observable computarizado que se repite a través de los artículos en el usuario de


la compra y añade el precio del total acumulado. Esta función se ejecuta cada vez que el
cartItems se cambia matriz observable.

El showProducts función restablece el availableProducts matriz con la lista de elementos para la


categoría seleccionada.

Los addItem y removeItem funciones agregar y quitar el elemento seleccionado hacia y


Del cartItems array, respectivamente.

74 | Capítulo 10: Un ejemplo práctico


Y, por último, los fadeIn y Fadeout funciones aprovechan algunos anima- básica jQueryUI ción
funciones para hacer la adición y la eliminación de la cesta elementos un poco de fantasía.

Ejemplo 10-3. data.js

var categories = [
new Category(1, "Books", [
new Book("1", "Book 1", "Book 1 Description", 1.99), new
Book("2", "Book 2", "Book 2 Description", 2.99), new Book("3",
"Book 3", "Book 3 Description", 3.99)
]),
new Category(2, "Movies", [
new Movie("Movie 1", "Movie 1 Description", 120, 4.99), new
Movie("Movie 2", "Movie 2 Description", 160, 5.99), new Movie("Movie
3", "Movie 3 Description", 110, 6.99)
]),
new Category(3, "Music", [
new Album("Artist 1", "Album 1", 10, 4.99), new
Album("Artist 2", "Album 2", 8, 7.99), new Album("Artist
3", "Album 3", 12, 6.99)
])
];

Tres categorías diferentes se crean: Libros, películas y música. Cada elemento de la categoría y la
propia categoría se crean mediante la creación de nuevos objetos (Category, book , Movie ,
y Album ). Estos se muestran con el classes.js archivo en el Ejemplo 10-4 .

Ejemplo 10-4. classes.js

function Category(id, name, items) {


this.id = id; this.name = name;
this.items = items;
};

function Book(isbn, title, description, price) {


var self = this;

self.isbn = isbn; self.title = title;


self.description = description; self.price =
price;

self.getTitle = function() {
return self.title;
};

self.getDescription = function() {
return self.description;
};

La construcción de una cesta de la compra | 75


};

function Movie(title, description, movie_length, price) {


var self = this;

self.title = title; self.description =


description; self.movie_length = movie_length;
self.price = price;

self.getTitle = function() {
return self.title;
};

self.getDescription = function() {
return 'Summary: ' + self.description + '<br/>' +
'Movie length: ' + self.movie_length;
};
};

function Album(artist, name, number_of_songs, price) {


var self = this;

self.artist = artist;
self.name = name;
self.number_of_songs = number_of_songs;
self.price = price;

self.getTitle = function() {
return self.name;
};

self.getDescription = function() {
return 'Artist: ' + self.artist + '<br/>' +
'Number of songs: ' + self.number_of_songs;
};
};

La categoría de objeto acepta tres parámetros: id, name , y una variedad de artículos . Estas son los
elementos que se utilizan para rellenar los availableProducts gama observable cuando se selecciona
una categoría.

El restante objetos- libro , Movie y álbum -son ligeramente diferente en lo que respecta a lo que los
datos que aceptan como parámetros. Esto permite que los datos se estructuran diferente en función
del tipo de producto que es; Sin embargo, cada objeto tiene tres compartían
propiedades: price, getTitle y getDescription . Dentro de cada objeto, el getTitle
y getDescription funciones se utilizan para determinar la forma en la salida debe ser formateado
cuando se muestra el artículo. Esto simplifica el código HTML original para evitar procesamiento
condicional cuando se muestra un elemento.

76 | Capítulo 10: Un ejemplo práctico


Cuando se ejecuta este ejemplo, se muestra una lista de las tres categorías. Cuando una categoría es
seleccionado, la lista de productos para esa categoría se mostrará con un botón para añadir ese
artículo a la cesta. Cuando se hace clic en el botón de adición, una animación jQuery se utiliza para
desvanecerse en el producto al carrito de la compra del usuario. Tanto el número de elementos y el
total acumulado se actualizan para reflejar el elemento recién añadido. Cuando se añade el artículo
al carro, hay es un botón de eliminar. Al hacer clic en este botón no la inversa de complemento; el
elemento se elimina con otra animación jQuery, y el recuento de elementos total del carrito y se
actualizan de nuevo.

Aunque el ejemplo parece sencillo, la funcionalidad es extremadamente mancha de aspecto con


Knockout actualización automática de la interfaz de usuario con cada acción que el usuario toma.

Lo dejo a usted para construir la forma final (enlaces de datos y todo) para recoger el usuario de el
contacto y la información de tarjeta de crédito para terminar la comprobación de su carrito de la
compra!

La construcción de una cesta de la compra | 77


CAPÍTULO 11
Próximos pasos

La simplicidad de Knockout

El último capítulo significó la conclusión de los ejemplos de código de este libro. Mientras yo
estaba trabajando en el plan para este libro, pensé que el último capítulo contendría un significante
de cantidad de más de código. Un carro de compras no es una cosa fácil de construir, ya que
incluye muchos componentes, tales como categorías de navegación, productos y artículos de
seguimiento el usuario añade a su carrito de compras. Realmente espero que esto demuestre cómo
KnockoutJS encaja en sus proyectos y hace que el mantenimiento de un estado a través de una
única página web realmente fácil. El modelo de vista final para el carrito de la compra fue de
menos de 40 líneas de código (Incluyendo saltos de línea para facilitar la lectura)! El HTML que
realiza los datos necesarios encuadernaciones era también menos de 40 líneas de código! La forma
Knockout trae esto juntos las interacciones del usuario con simples hace que las personas de 80
líneas de código muy potente.

El enfoque de este libro fue sobre el aprendizaje de los entresijos de la eliminatoria. Creo que cada
característica estaba cubierta. Si tuviera que avanzar en el ejemplo carrito de la compra, me
concentro en el diseño. La implementación actual parece bastante simple, pero con un poco de
estilo, que llevaría rápidamente en un aspecto totalmente nuevo con poco o ningún cambio en el
JavaScript. Por supuesto, el diseño no es mi fuerte, así que lo dejo a los diseñadores!

Antes de utilizar Knockout, he construido interfaces de usuario del lado del cliente pesadas, y yo
puedo asegurar que participa significativamente más de 80 líneas de código. Se jQuery compleja
involucrada añadir y quitar de forma dinámica los elementos HTML, o agregar y quitar CSS
Clases Compare esto con Knockout, donde se tarda unos pocos caracteres de enlaces de datos a
lograr la misma cosa!
79
Contexto del usuario
Tanto como usuario de Internet y un desarrollador web, estoy constantemente pensando en
usabilidad y las interfaces de usuario. Uno de los ejes principales presto mucha atención es el
usuario de contexto.

Por ejemplo, es bastante común para datos adicionales para ser cargadas a través de AJAX. Veo
dos enfoques comunes para este: Carga de los datos y la adición por debajo de la existente
contenga, o carga de los datos y reemplazar el contenido existente. El ex perfectamente mantiene
mi contexto. En este último ejemplo, cuando se sustituye el contenido, que es extremadamente
importante que, como usuario, mi contexto es de esperar de nuevo al comienzo de la nuevos
contenidos y no en la parte inferior de los nuevos contenidos en la acción para cargar el El
contenido era.

Otro ejemplo común es con una forma. Estos son sometidos a menudo a través de AJAX (como se
se muestra en este libro). Cuando se muestra un error, es importante pensar en donde se muestra
este mensaje. Probable contexto del usuario sería la parte inferior del formulario donde el botón de
envío es. Si se muestra un error en la parte superior de la pantalla, probablemente pasará
desapercibido por el usuario.

Al crear aplicaciones dinámicas del lado del cliente, Knockout hace que sea muy fácil de mantener
cualquier contenido en la pantalla hacia arriba al día, que implica especialmente la interacción del
usuario. Aunque Knockout hace que sea fácil, es importante que el contexto se mantenga
correctamente. Más a menudo, no se requiere ningún esfuerzo, pero asegúrese de tener esto en
cuenta cuando se altera gran porciones de los contenidos. Esto hará que la pantalla para cambiar el
tamaño, lo que podría perder el contexto apropiado.

Vuelve a cargar la página para actualizaciones de contenido


Recargas de página completa son una cosa del pasado! Si el contenido tiene que ser actualizado,
Knockout le da ninguna excusa para no hacerlo. Usted simplemente tiene que hacer que los datos
observables y actualizarlo cuando cambia.

Tenía una gran discusión acerca de esto el otro día con un compañero de trabajo. Tuvimos la
opción a:

• Actualizar la página y perder el contexto del usuario (y no ser mancha de aspecto)


• Realizar un bucle y llevar a cabo una instrucción if / else dentro del bucle para actualizar el
contenido observada

Que no quieren perder el contexto del usuario, se eligió la segunda opción. Claro, se tomó 2
minutos para escribir el bucle y la sentencia condicional en comparación con 15 segundos para
escribir location.reload. Sin embargo, el resultado de la elección de la opción dos es un usuario
mucho mejor experimento La Al hacer clic en el botón hace que la interfaz de usuario para
actualizar, proporcionando (casi) retroalimentación instantánea después de la acción tiene lugar.

80 | Capítulo 11: Los próximos pasos


Esto también proporciona la capacidad para otras mejoras en la interfaz de usuario ordenada como
animaciones. Esto se demostró en el carrito de la compra con un fundido de salida y arrastrando
cesta productos ya que fueron añadir y eliminar, respectivamente. Me parece que este es un gran
interfaz más agradable en lugar de ser trasladado a una nueva página para mostrar la misma
información.

Conclusión
Espero que este libro le haya proporcionado una gran cantidad de ejemplos útiles. Mientras
pensaba cada característica, he tratado de pensar en cómo lo uso a diario en mis proyectos. Y donde
he podido, he pasado en las pequeñas complejidades que he tropezamos mientras utilizando
Knockout dentro de un gran proyecto.

Conclusión | 81
Índice

Symbols
$data variable, 11
$index variable, 12
$parent variable, 11, 15
$parents variable, 11, 15
$root variable, 11, 15

A
afterAdd method, 15 afterMove method, 15 afterRender method, 15
AJAX, 55, 80
alert statements, 22 arrays
in shopping cart example, 71 observable arrays, 22 selectedOptions binding and, 31

B
beforeMove method, 15 beforeRemove method, 15 brackets, 28

C
callback methods, 15-17
CamelCase, 8 checked binding, 31 click binding, 33
computed observables, 22 conditional statements, 9, 28, 68
CSS
classes, binding of, 8 display property, 24
display: none style, 26 custom data bindings, 40 custom functions, 47

D
data
receiving, 58-61 sending, 55-58
data binding
adding multiple, 8 condition-based, 9, 28 creating custom, 40
CSS classes and styles, 8
HTML attributes, 8
HTML data, 7
specifying with HTML comments, 1 syntax for, 1
two-way, 31
data binding context foreach bindings, 12-15
foreach callbacks (events), 15-17 navigating between, 11 parent/child hierarchy, 11
root context, 11
with bindings, 17-19 data validation, 36
data-bind attribute, 1, 7 disable binding, 31 display property, 24
DOM (Document Object Model), 27, 33 drop-down lists, 31, 34

E
elements

83
adding/removing, 26 form elements, 31-38 showing/hiding, 24
enable binding, 31 event binding, 33 events
foreach callbacks, 15-17 used with forms, 33

F
foreach bindings, 12-15
foreach callbacks (events), 15-17 form elements, 33
forms
adding to/editing, 68
bindings for form elements, 31 enabling/disabling text boxes, 31 event data bindings, 33
form validation, 36 listening for changes, 34 sending data from, 55-58 two-way bindings for, 31 working with, 31-38
functions
adding custom to observables, 49 conditional, 68
creating custom, 47

H
hasFocus binding, 33
HTML
binding attributes, 8 binding HTML data, 7
data bindings via comments, 1 data-bind attribute, 1
displaying lists in HTML tables, 13 for shopping cart example, 71 repeating sections on arrays, 12 reusing templates, 43

I
if/ifnot data bindings, 26 inheritance, 49
input element, 31, 33

J
JavaScript, 4, 43, 63 jQuery, 17, 55
jQuery Validation Plugin, 36

K
key/value pairs, 55 keypress event, 33
KnockoutJS
basic implementation
data binding example, 2 data binding syntax, 1
MVVM (Model-View-ViewModel), 3 overview of, 1
ViewModel creation, 3
ViewModel object orientation, 4
ViewModels with parameters, 5 benefits of, vii, 1
common uses of, 2 history of, vii installing, vii plugins for, 63 purpose of, 1 templates, 43
version changes, viii ko.applyBindings function, 3, 11, 14, 39 ko.mapping.fromJS, 64 ko.mapping.fromJSON function, 67
ko.observable function, 21
ko.toJS function, 57 ko.toJSON function, 57

L
listening mode, 23 lists, 31

M
mapping plugin
map from an object, 63 map from JSON, 65 purpose of, 63
Model-View-Controller (MVC) pattern, 3
Model-View-ViewModel (MVVM) pattern, 3 mouseover event, 33
Mozilla Developer Network (MDN), 4 multiselect lists, 31

O
object-oriented programming (OOP), 4 observables
accessing, 22

84 | Index

adding custom functions to, 49 adding/removing elements, 26 best use of, 28, 68
combining into single object, 22 computed observables, 22 creating observable variables, 21 defining, 21
extending with custom functions, 47 instantiating observable arrays, 22 partial application of, 28 pureComputed
observables, 23 purpose of, 21
rate-limiting, 51-53 selectedOptions binding and, 31 showing/hiding elements, 24 storing server responses with, 58
subscribe function, 34
updates with textInput vs. value bindings,
31
use of brackets and, 28 visible data binding, 24
onChange events, 32 options binding, 31

P
parent/child hierarchy, 11 parentheses, 43
plugins, 63
(see also mapping plugin) POST request method, 55 properties
dynamically changing, 21 (see also observables)
observing/ignoring specific, 68 storing server responses with, 58
pureComputed observables, 23

R
rate-limiting observables, 51-53
REST APIs, 55

S
screen flickers, preventing, 26 select element, 31 selectedOptions binding, 31 self variable, 5
server-side interaction choices for, 55
mapping from, 65 receiving data, 58-61 sending data, 55-58
shopping cart example addItem, 74
animated enhancements for, 81 arrays needed, 71 availableProducts, 74
benefits of KnockoutJS for, 79 cartItems, 74
cartTotal, 74
core features of, 71 current running total, 71 design improvements, 79 fadeIn/fadeOut functions, 75
HTML for, 71 output, 73 removeItem, 74 showProducts, 74
showExtraData variable, 26, 28 sleeping mode, 23
style bindings, 8 submit binding, 33 submit data binding, 36 subscribe function, 34

T
templates, 43, 73
text boxes, enabling/disabling, 31 textarea element, 31, 40
textInput binding, 31 this variable, 5
TinyMCE WYSIWYG editor, 40 two-way bindings, 31

U
unobtrusive form validation, 36 updateObservable function, 25 user interfaces (UIs)
animated enhancements for, 81 content updates, 80
dynamic, vii
role of KnockoutJS, 1 user's context, 80

V
validate function, 37 validation, of forms, 36

Index | 85

value binding, 31 valueUpdate property, 32 var self = this, 5


variables
$data variable, 11
$index variable, 12
$parent variable, 11, 15
$parents variable, 11, 15
$root variable, 11, 15
creating observable variables, 21 selectedOptions binding and, 31 showExtraData, 26
ViewModels
binding multiple, 39 creating, 3
displaying name property within, 2 dynamically displaying content within, 2 instantiating as a class, 14
object-oriented, 4 with parameters, 5 wrapping in a class, 5
visible data binding, 24, 26

W
with bindings, 17-19
WYSIWYG editors, 40

X
XAMPP server, 60

86 | Índice
Sobre el Autor
El autor de 20 recetas para la programación MVC 3 y 20 Recetas para la
ProgramaciónPhoneGap de O'Reilly Media, Jamie Munro ha sido el desarrollo de sitios
web y web las solicitudes de más de 15 años. Durante los últimos 8 años, Jamie ha estado
actuando como una ventaja desarrollador de tutoría a los desarrolladores más jóvenes para
mejorar sus habilidades de desarrollo web.

Tomando su amor por la tutoría gente, Jamie empezó su carrera de escritor en su personal
un blog en 2009. Como el blog de Jamie creció en el éxito, se volvió a su pasión por escrito
libros sobre el desarrollo web con la esperanza de que sus muchos años de experiencia
podría ser pasó a usted, el lector.

Colofón
El animal en la portada de Knockout.js es un canguro de árbol negro ( Dendrolagus
ursinus ), un marsupial que habita en las selvas tropicales de Nueva Guinea, lejos
Northeastern Queensland, y algunas de las islas de la región. El canguro del árbol es el
único verdadero miembro arbórea de la familia de los canguros.

El canguro del árbol negro se reconoce fácilmente por sus largas orejas peludas, que son
únicos entre las especies de árboles canguro. Pelo largo y negro cubre la parte posterior y la
cola, mientras que la piel en la parte inferior es de color café. Su cara es más distintivo de
color marrón, con blanco o a veces las mejillas rojas y una garganta blanca.
El canguro del árbol negro es conocido por ser en su mayoría solitarios y nocturnos,
emergiendo después oscuro para alimentarse de su alimento preferido de las hojas y los
frutos.

los pueblos indígenas en todo el rango del canguro arborícola cazan los animales para la
alimentación, a veces el uso de perros para rastrearlos. Para varias especies, la caza por sí
solo tiene impulsado estos marsupiales cerca de la extinción.

Muchos de los animales en peligro de extinción cubiertas O'Reilly están; todos ellos son
importantes para el mundo. Para obtener más información sobre cómo puede ayudar, ir
a animals.oreilly.com .

La imagen de portada es de Riverside Historia Natural. Las fuentes de presentación son


URW Tipo escritor y Guardian Sans. La fuente del texto es Adobe Minion Pro; la fuente es
encabezada Adobe Myriad condensada; y la fuente de código es de Dalton Maag Ubuntu
Mono.

Vous aimerez peut-être aussi