El primer
artículo sobre
dap , obviamente, no se convirtió en mi éxito de escritura: la gran mayoría de los comentarios se redujeron a "niasilil" y "niasilil, pero lo condeno". Y el premio para
el único comentario constructivo de alto nivel va a
OldVitus , para que le aconsejen demostrar el dap usando TodoMVC como ejemplo, para que haya algo para comparar. ¿Qué haré en este artículo?
TodoMVC , si alguien no lo sabe, este es un mundo de llamadas de interfaz de usuario tan estándar, que le permite comparar soluciones al mismo problema, la "lista de tareas" condicional, usando diferentes marcos. La tarea, con toda su simplicidad (su
solución en dap breaks "en una pantalla"), es muy ilustrativa. Por lo tanto, usando su ejemplo, intentaré mostrar cómo las tareas típicas para un front-end web se implementan usando dap.
No busqué ni estudié la descripción formal del problema, pero decidí simplemente revertir uno de los ejemplos. El backend de este artículo no es interesante para nosotros, por lo que no lo escribiremos nosotros mismos, pero use
uno de los listos del sitio
www.todobackend.com , y de ahí tomaremos
un cliente de ejemplo y un
archivo CSS estándar.
Para usar dap no necesita descargar ni instalar nada. No
npm install
y eso es todo. No es necesario crear ningún proyecto con una estructura de directorio específica, manifiestos y otros atributos de éxito de TI. Suficiente editor de texto y navegador. Para depurar solicitudes XHR, también puede necesitar un servidor web, uno bastante simple, como esta
extensión para Chrome. Toda nuestra interfaz consistirá en un único archivo .html (por supuesto, en referencia al script del motor dap y al archivo CSS estándar TodoMVC)
Entonces, desde cero.
1. Crear un archivo .html
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Todo -- dap sample</title> <link rel="stylesheet" href="https://www.todobackend.com/client/css/vendor/todomvc-common.css"/> <script src="https://dap.js.org/0.4.js"></script> </head> <body> <script> </script> </body> </html>
La preparación html habitual en la que incluimos el archivo CSS, amablemente proporcionado por el sitio
www.todobackend.com y el motor dap, no menos amablemente proporcionado por el sitio
dap.js.org2. Copie la estructura DOM del ejemplo original
Para usar el archivo CSS estándar sin alteraciones, nos adheriremos a la misma estructura DOM que el
ejemplo original . Ábralo en el navegador Chrome, presione Ctr + Shift + I, seleccione la pestaña Elementos y vea que la aplicación en sí está en la
section id="todo-app">
elemento
section id="todo-app">

Al abrir este subárbol uno por uno, reescribimos su estructura en nuestro archivo .html. Ahora estamos simplemente dibujando de una manera rápida, y no escribiendo código, así que solo escribimos las firmas de los elementos entre 'comillas simples' y entre paréntesis de sus hijos. Si no hay niños, dibujamos corchetes vacíos. Monitoreamos los índices y el equilibrio de los corchetes.
Nota: los elementos repetidos (por ejemplo, aquí son elementos
LI
) los escribimos a la estructura una vez, incluso si hay varios de ellos en el original; obviamente, estos son matrices del mismo patrón.
El formato de firma, creo, es comprensible para cualquiera que haya escrito HTML y CSS con sus manos, por lo que no me detendré en detalles por ahora. Solo puedo decir que las etiquetas están escritas en letras MAYÚSCULAS, y la ausencia de una etiqueta es equivalente a la presencia de una etiqueta DIV. La abundancia de elementos # (con id) se debe a los detalles del archivo CSS incluido, que utiliza principalmente selectores de id.
3. Recuerde que el programa dap es Javascript
Para salvarnos de los corchetes innecesarios en el código, el motor dap inyecta varios métodos directamente en
String.prototype
(soy consciente de que implementar sus métodos en objetos estándar es ayahay, pero ... en resumen, lo hemos aprobado), que convierte la cadena de firma en dap plantilla Uno de estos métodos es
.d(rule, ...children)
. El primer argumento que toma es una regla de generación (regla
d ), y el resto de los argumentos son un número arbitrario de hijos.
En base a este nuevo conocimiento, agregamos nuestro código para que en lugar de cada llave de apertura tengamos la secuencia
.d(""
, y antes de cada comilla simple de apertura, excepto la primera, hay una coma. Hack de vida: puede usar el reemplazo automático.
'#todoapp'.d("" ,'#header'.d("" ,'H1'.d("") ,'INPUT#new-todo placeholder="What needs to be done?" autofocus'.d("") ) ,'#main'.d("" ,'#toggle-all type=checkbox'.d("") ,'UL#todo-list'.d("" ,'LI'.d("" ,'INPUT.toggle type=checkbox'.d("") ,'LABEL'.d("") ,'BUTTON.destroy'.d("") ) ) ) ,'#footer'.d("" ,'#todo-count'.d("") ,'UL#filters'.d("" ,'LI'.d("") ) ,'#clear-completed'.d("") ) )
Voila! Tenemos un árbol de llamadas al método
.d
, que está listo para transformarse en una plantilla dap. Las cadenas vacías
""
son las semillas de futuras reglas d, y los childs son argumentos separados por comas. Formalmente, este es un programa dap válido, aunque aún no completamente con el escape que necesitamos. ¡Pero ya se puede lanzar! Para hacer esto, después del cierre del corchete raíz, agregue el método
.RENDER()
. Este método, como su nombre lo indica, representa la plantilla resultante.
Entonces, en esta etapa, tenemos un archivo .html con este contenido:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Todo -- dap sample</title> <link rel="stylesheet" href="https://www.todobackend.com/client/css/vendor/todomvc-common.css"/> <script src="https://dap.js.org/0.4.js"></script> </head> <body> <script> '#todoapp'.d("" ,'#header'.d("" ,'H1'.d("") ,'INPUT#new-todo placeholder="What needs to be done?" autofocus'.d("") ) ,'#main'.d("" ,'#toggle-all type=checkbox'.d("") ,'UL#todo-list'.d("" ,'LI'.d("" ,'INPUT.toggle type=checkbox'.d("") ,'LABEL'.d("") ,'BUTTON.destroy'.d("") ) ) ) ,'#footer'.d("" ,'#todo-count'.d("") ,'UL#filters'.d("" ,'LI'.d("") ) ,'#clear-completed'.d("") ) ) .RENDER() // dap </script> </body> </html>
Puede
abrirlo en un navegador para asegurarse de que se generen elementos DOM, se apliquen estilos CSS, solo queda llenar esta plantilla con datos.
4. Obtenga los datos
Vamos a
la página original , abrimos la pestaña Red en las herramientas, activamos el filtro XHR y vemos de dónde provienen los datos y de qué forma.


Vale, vale. La lista de tareas se toma directamente de
todo-backend-express.herokuapp.com como una matriz json de objetos. Genial
Para recibir datos, dap tiene un convertidor incorporado
:query
que asincrónicamente "convierte" la URL en los datos recibidos de ella. No escribiremos la URL en sí directamente en la regla, pero la designaremos con
todos
constantes; entonces todo el diseño de minería de datos se verá así:
todos:query
y escriba la constante
todos
en el diccionario, en la sección
.DICT
, justo antes de
.RENDER()
:
'#todoapp'.d("" ... ) .DICT({ todos : "https://todo-backend-express.herokuapp.com/" }) .RENDER()
Una vez recibido el conjunto de
todos
, construimos una lista de tareas a partir de él: para cada caso tomamos el nombre del campo
.title
y lo escribimos en el elemento
LABEL
, y desde el campo
.completed
tomamos el signo de "integridad" y escribimos en la propiedad
checked
del elemento de
checked
INPUT.toggle
. Se hace así:
,'UL#todo-list'.d("*@ todos:query"
Actualizamos nuestra página en el navegador y ... si la inicia desde el sistema de archivos, no pasa nada. El problema es que los navegadores modernos no permiten solicitudes XHR entre dominios de documentos locales.

Es hora de ver nuestra página a través de http, utilizando cualquier servidor web local. Bueno, o si no está listo para escribir dap con sus propias manos, vea versiones secuenciales de la página usando mis enlaces (no olvide mirar la fuente; en Chrome, esto se hace usando Ctrl + U)
Entonces, vamos
a nuestra página en http: // y vemos que los datos están llegando, la lista está siendo construida. Genial ¡Ya has dominado el
*
y los operadores
!
, convertidor
:query
, constantes y acceso a los campos del elemento de matriz actual. Mire nuevamente el código resultante. ¿Todavía te parece ilegible?
5. Añadir estado
Quizás ya haya intentado hacer clic en las marcas de verificación en la lista de tareas pendientes. Las casillas de verificación cambian de color, pero, a diferencia del original, el elemento primario
LI
no cambia su estilo (el "trabajo completado" debe volverse gris y tachado, pero esto no sucede): las cosas no cambian su
estado . Pero estos elementos aún no tienen ningún estado y, en consecuencia, no pueden cambiarlo. Ahora lo arreglaremos.
Agregue un estado de "finalización" al elemento
LI
. Para hacer esto, defina la
variable de estado $completed
en su regla d. Al
INPUT.toggle
, que puede cambiar este estado, asignaremos una regla de reacción apropiada (
ui-rule ), que establecerá la variable
$completed
de acuerdo con su propia marca
checked
("daw está activado"). Dependiendo del estado de
$completed
elemento
LI
habilitará o deshabilitará la clase CSS "completada".
,'UL#todo-list'.d("*@ todos:query" ,'LI'.d("$completed=.completed"
Tales manipulaciones con clases CSS son bastante comunes, ¡así que hay un operador especial en dap para ellas!
Tenga en cuenta que hacemos esto en la
regla a (de la palabra acumular). ¿Por qué no en la regla d? La diferencia entre estos dos tipos de reglas es que, al actualizar, la regla d reconstruye completamente el contenido del elemento, eliminando las antiguas y las nuevas, mientras que la regla a no toca el contenido existente del elemento, sino que "agrega" el resultado a lo que ya está allí. Cambiar un solo atributo de un elemento
LI
no requiere reestructurar el resto de su contenido, por lo que es más racional hacerlo en la regla a.
Nos fijamos en el
resultado . Ya es mejor: hacer clic en las casillas de verificación cambia el estado del elemento de tarea pendiente correspondiente y, de acuerdo con este estado, el estilo visual del elemento también cambia. Pero todavía hay un problema: si la lista de tareas completadas estaba inicialmente presente, no serán grises, porque de forma predeterminada la regla a no se ejecuta cuando se genera el elemento. Para ejecutarlo incluso durante la generación, agregamos el operador
a!
A la regla d del elemento
LI
,'LI'.d("$completed=.completed; a!"
Nosotros miramos . Esta bien Con el estado de
$completed
resuelto. Los casos completados se estilizan correctamente tanto en el arranque inicial como durante el cambio manual posterior.
6. Edición de nombres de casos
De vuelta al
original . Al hacer doble clic en el nombre del caso, se activa el modo de edición, en el que se puede cambiar este nombre. Allí se implementa de tal manera que la plantilla del modo de vista "ver" (con un daw, un título y un botón de eliminación) está completamente oculta, y se muestra el elemento
INPUT class="edit"
. Lo haremos un poco diferente: ocultaremos solo el elemento
LABEL
, ya que los otros dos elementos no interfieren con nuestra edición. Simplemente agregue la clase de
view
al elemento
LABEL
.
Para el estado "edición", defina la variable
$editing
en el elemento
LI
. Inicialmente, se restablece (estado), se activa mediante
dblclick
en el elemento
LABEL
y se desactiva cuando el elemento
INPUT.edit
está desenfocado. Entonces escribimos:
,'LI'.d("$completed=.completed $editing=; a!"
Ahora podemos editar los nombres de los casos.
7. Enviar datos al servidor
Ok, ya podemos editar cosas en el navegador, pero estos cambios también deben transferirse al servidor. Vemos cómo lo hace el original:

Los cambios se envían al servidor utilizando el método PATCH con una determinada URL del formulario
http://todo-backend-express.herokuapp.com/28185
, que, obviamente, es único para cada caso. El servidor indica esta URL en el campo
.url
para cada caso de la lista. Es decir, todo lo que se requiere de nosotros para actualizar el caso en el servidor es enviar una solicitud PATCH a la dirección especificada en el campo
.url
, con los datos modificados en formato JSON:
,'INPUT.edit' .d("? $editing; !! .title@value") .ui(".title=#.value; (@method`PATCH .url (@Content-type`application/json)@headers (.title):json.encode@body):query") .e("blur","$editing=")
Aquí usamos el mismo conversor
:query
, pero en una versión más detallada. Cuando
:query
se aplica a una cadena simple, esta cadena se trata como una URL y se ejecuta una solicitud GET. Si
:query
recibe un objeto complejo, como en este caso, lo trata como una descripción detallada de la solicitud que contiene los campos
.url
,
.headers
,
.headers
y
.headers
, y ejecuta la solicitud de acuerdo con ellos. Aquí, inmediatamente después de actualizar
.title
enviamos al servidor una solicitud de PATCH con este
.title
actualizado
Pero hay un matiz. Obtenemos el campo
.url
del servidor, se ve más o menos así:
http://todo-backend-express.herokuapp.com/28185
, es decir, el protocolo http: // está codificado si nuestro cliente también está abierto a través de http: // entonces todo está bien. Pero si el cliente está abierto a través de https: //, surge un problema: por razones de seguridad, el navegador bloquea el tráfico http desde la fuente https.
Se resuelve simplemente: si elimina el protocolo de
.url
, la solicitud pasará por el protocolo de la página. Así que hagámoslo: escriba el convertidor apropiado -
dehttp
, y pasaremos
.url
través de él. Los convertidores personalizados (y otras funciones) se
.FUNC
en la sección
.FUNC
:
.ui(".title=#.value; (@method`PATCH .url:dehttp (@Content-type`application/json)@headers (.title):json.encode@body):query") ... .FUNC({ convert:{
También tiene sentido poner el objeto de encabezado en el diccionario para que pueda usarse en otras consultas:
.ui(".title=#.value; (@method`PATCH .url:dehttp headers (.title):json.encode@body):query") ... .DICT({ todos : "//todo-backend-express.herokuapp.com/", headers: {"Content-type":"application/json"} })
Bueno, para el feng shui completo, utilizaremos otra propiedad útil del convertidor
:query
: codificación automática del cuerpo de la solicitud en json de acuerdo con el
Content-type:application/json
encabezado de
Content-type:application/json
. Como resultado, la regla se verá así:
.ui(".title=#.value; (@method`PATCH .url:dehttp headers (.title)):query")
Entonces,
mira . De acuerdo, los nombres de los casos ahora cambian no solo en el navegador, sino también en el servidor. Pero! No solo el nombre del caso puede cambiar, sino también su estado de finalización:
completed
. Por lo tanto, también debe enviarse al servidor.
Puede agregar la misma solicitud PATCH al elemento
INPUT.toggle
, solo envíe
(.completed)
lugar de
(.completed)
:
,'INPUT.toggle type=checkbox' .d("#.checked=.completed") .ui("$completed=#.checked; (@method`PATCH .url:dehttp headers (.completed:?)):query")
Y puede poner esta solicitud de PARCHE "entre paréntesis":
,'LI'.d("$completed=.completed $editing= $patch=; a!"
Aquí está la cosa. Las reglas de reacción pertenecen al grupo de "reglas superiores", que se ejecutan "de abajo hacia arriba", desde el descendiente hasta el padre, hasta la raíz misma (esta secuencia puede interrumpirse si es necesario). Esto es algo así como los eventos emergentes en el DOM. Por lo tanto, algunos fragmentos de la reacción común a varios descendientes pueden asignarse a un antepasado común.
Específicamente, en nuestro caso, la ganancia de dicha delegación no es particularmente notable, pero si hubiera más campos editables, poner esta solicitud voluminosa (por estándares dap, por supuesto) en una regla general ayudaría enormemente a mantener el código simple y legible. Por eso lo recomiendo.
Buscamos : ahora tanto los cambios de nombre como los cambios de estado se envían al servidor.
En el siguiente artículo, si está interesado, considere agregar, eliminar y filtrar casos. Mientras tanto, puede ver el
resultado final y otros ejemplos de código dap en
dap.js.org/docs