Portar su aplicación web de JavaScript puro a Vue.js

Vue.js es un marco de desarrollo de aplicaciones web. Tiene un sistema de reactividad que permite al desarrollador simular el estado de la aplicación y administrarla. Como resultado, cuando los datos cambian, se reflejan automáticamente en la interfaz de usuario, mientras que el desarrollador no necesita acceder al DOM. Si crea aplicaciones usando JavaScript puro o jQuery, esto significa que debe acceder explícitamente a los elementos DOM y actualizarlos para reflejar los cambios en el estado de la aplicación en la interfaz, por ejemplo, mostrar algunos datos en una página web.


En el caso de proyectos grandes, administrar manualmente la sincronización del estado y la interfaz de la aplicación no es una tarea fácil. El autor del material, cuya traducción presentamos hoy a su atención, quiere compartir algunos resultados de su investigación dirigida a comparar dos versiones de una aplicación web progresiva con Hoodie , que es una lista de compras . La versión básica de esta aplicación está escrita en JS puro (en este artículo puede encontrar detalles al respecto). Aquí se mostrará la traducción de la aplicación a Vue.js con un examen de aprobación de las características básicas de este marco y con un análisis de lo que sucedió al final.


Aplicación lista

Si desea trabajar en este material, puede descargar el código fuente de la aplicación en JS puro y seguir al autor, procesándolo con Vue.

Agregar productos a la lista


▍JavaScript


La aplicación permite al usuario agregar productos a su lista de compras. Esto se hace en el archivo index.html en la carpeta pública . El marcado está contenido en las líneas 92-124 de este archivo. Ahí está ella.

 <div> <div class="mdl-grid center-items"> <div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label">     <input class="mdl-textfield__input" type="text" id="new-item-name">     <label class="mdl-textfield__label" for="new-item-name">Item Name</label> </div> <div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label">     <input class="mdl-textfield__input" type="number" id="new-item-cost">     <label class="mdl-textfield__label" for="new-item-cost">Item Cost</label> </div> <div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label">     <input class="mdl-textfield__input" type="number" id="new-item-quantity">     <label class="mdl-textfield__label" for="new-item-quantity">Quantity</label> </div> </div> <div class="mdl-grid center-items"> <button id="add-item" class="mdl-button mdl-js-button mdl-button--raised mdl-button--colored">     Add Item </button> </div> </div> 

El código para procesar y guardar datos está en el archivo public/js/src/index.js . La función saveItems() en la línea 28 es responsable de recopilar los valores de los controles utilizados para ingresar datos y de guardar esos datos. Esta función está vinculada al evento de click del click add-item . Aquí está el código en cuestión.

 function saveNewitem() { let name = document.getElementById("new-item-name").value; let cost = document.getElementById("new-item-cost").value; let quantity = document.getElementById("new-item-quantity").value; let subTotal = cost * quantity; if (name && cost && quantity) {   hoodie.store.withIdPrefix("item").add({     name: name,     cost: cost,     quantity: quantity,     subTotal: subTotal   });   document.getElementById("new-item-name").value = "";   document.getElementById("new-item-cost").value = "";   document.getElementById("new-item-quantity").value = ""; } else {   let snackbarContainer = document.querySelector("#toast");   snackbarContainer.MaterialSnackbar.showSnackbar({     message: "All fields are required"   }); } } document.getElementById("add-item").addEventListener("click", saveNewitem); 

▍Vue


Antes de comenzar a reelaborar este proyecto con Vue, debe conectar el marco a la página. En nuestro caso, en el archivo index.html , esto se hace de la siguiente manera:

 <script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script> 

Además, se agregó un elemento <div> con la app identificación, que incluirá todos los elementos de la página ubicados dentro de la etiqueta del body . Esto se debe a que cuando se inicializa la instancia de Vue, se debe informar al marco sobre qué parte de la página debe administrar. En este caso, informamos al marco que debe tratar con todo lo que está en este bloque.

Ahora comencemos a traducir el proyecto a Vue. Primero, modificamos el marcado para usar algunas directivas Vue. Las directivas Vue son atributos especiales con el prefijo v- . Así es como se ve el marcado actualizado.

 <form v-on:submit.prevent="onSubmit"> <div class="mdl-grid center-items">   <div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label">     <input class="mdl-textfield__input" type="text" id="new-item-name" v-model="name">     <label class="mdl-textfield__label" for="new-item-name">Item Name</label>   </div>   <div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label">     <input class="mdl-textfield__input" type="number" id="new-item-cost" v-model.number="cost">     <label class="mdl-textfield__label" for="new-item-cost">Item Cost</label>   </div>   <div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label">     <input class="mdl-textfield__input" type="number" id="new-item-quantity" v-model.number="quantity">     <label class="mdl-textfield__label" for="new-item-quantity">Quantity</label>   </div> </div> <div class="mdl-grid center-items">   <button id="add-item" class="mdl-button mdl-js-button mdl-button--raised mdl-button--colored">     Add Item   </button> </div> </form> 

La directiva v-on utilizada aquí se utiliza para escuchar eventos DOM. En el fragmento de código anterior, se usa en el elemento de formulario para escuchar el evento de submit . Además, utiliza el modificador .prevent , que le dice a la directiva v-on que llame a event.preventDefault() en el evento llamado. Utilizamos la directiva v-model para los elementos utilizados para la entrada de datos. Es necesario, tal como se aplica a los elementos de formulario, para crear un enlace de datos bidireccional. Esto permite que el sistema seleccione automáticamente la forma correcta de actualizar un elemento según su tipo. Utilizamos el modificador .number para los elementos de cost y quantity .

Esto convierte automáticamente el tipo de valor del campo de entrada en un número. Esto se hace porque incluso si el tipo se establece en type=number , el valor siempre se devolverá como una cadena. Los modificadores utilizados aquí automatizan la ejecución de comprobaciones adicionales que anteriormente tenían que realizarse de forma independiente.

A continuación, cree un nuevo archivo, index-vue.js , en el que colocamos el código equivalente al de index.js , pero utilizando las capacidades de Vue. El código para este archivo se muestra a continuación. Esto crea una instancia de Vue con las propiedades necesarias para procesar eventos de formulario y recopilar datos.

 const vm = new Vue({ el: "#app", data: {   name: "",   cost: "",   quantity: "" }, methods: {   onSubmit: function(event) {     if (this.name && this.cost && this.quantity) {       hoodie.store.withIdPrefix("item").add({         name: this.name,         cost: this.cost,         quantity: this.quantity,         subTotal: this.cost * this.quantity       });       this.name = "";       this.cost = "";       this.quantity = "";     } else {       const snackbarContainer = document.querySelector("#toast");       snackbarContainer.MaterialSnackbar.showSnackbar({         message: "All fields are required"       });     }   } } }); 

Se creó una instancia de Vue en este fragmento de código, y se le pasó un objeto, diciéndole a Vue cómo configurar la aplicación. La propiedad el le dice al framework el identificador del elemento DOM, cuyo contenido será controlado por Vue, considerándolo su "territorio". Es dentro de este elemento que Vue considerará las directivas específicas del marco (y todo lo demás relacionado con Vue) y, en el proceso de inicialización del marco, configurará los enlaces y los controladores de eventos para la aplicación.

La propiedad de data contiene el estado de la aplicación. Todas las propiedades en el objeto que está disponible aquí se agregarán, al inicializar Vue, al sistema de respuesta del marco. Son las acciones de este sistema las que conducen a la actualización de la interfaz de usuario al cambiar los valores asociados con el DOM. Por ejemplo, la propiedad de name está vinculada al control de name utilizando la directiva v-model="name" . Esta directiva establece el enlace de datos bidireccional entre la propiedad y el control de tal manera que cuando se agrega algo al campo de entrada (o algo se elimina), la propiedad de name se actualiza. Como resultado, el contenido del campo de entrada refleja el estado actual de la propiedad de name .

Del mismo modo, el enlace funciona con otros controles.

La propiedad de methods contiene funciones. En el código anterior, se onSubmit() función onSubmit() , que se adjunta al evento de formulario submit() .

Mostrar datos guardados en una página


▍JavaScript


La función onSubmit guarda datos en una sudadera con capucha. Después de eso, debemos mostrar, en forma de tabla, los elementos agregados al repositorio. En el caso de una aplicación escrita en JS regular, el marcado correspondiente se ve así:

 <div class="mdl-grid center-items"> <table id="item-table" class="mdl-data-table mdl-js-data-table mdl-shadow--2dp">   <thead>     <tr>       <th class="mdl-data-table__cell--non-numeric">Item Name</th>       <th class="mdl-data-table__cell--non-numeric">Cost</th>       <th class="mdl-data-table__cell--non-numeric">Quantity</th>       <th class="mdl-data-table__cell">Sub-total</th>       <th class="mdl-data-table__cell--non-numeric">         <button class="mdl-button mdl-js-button mdl-button--icon">           <i class="material-icons">delete</font></i>         </button>       </th>     </tr>   </thead>   <tbody>   </tbody> </table> </div> <div class="mdl-grid center-items"> <div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label">   <input class="mdl-textfield__input" type="number" id="total-cost" readonly value="0">   <label class="mdl-textfield__label" for="cost">Total Item Cost</label> </div> </div> <script id="item-row" type="text/template"> <tr id='{{row-id}}'>        <td class="mdl-data-table__cell--non-numeric">{{name}}</td>   <td class="mdl-data-table__cell--non-numeric">{{cost}}</td>   <td class="mdl-data-table__cell--non-numeric">{{quantity}}</td>   <td class="mdl-data-table__cell">{{subTotal}}</td>   <td class="mdl-data-table__cell--non-numeric">         <button class="mdl-button mdl-js-button mdl-button--icon mdl-button--colored"         onclick="pageEvents.deleteItem('{{item-id}}')">         <i class="material-icons">remove</font></i>         </button>   </td> </tr> </script> 

La tabla contendrá datos agregados dinámicamente, por lo que aquí necesitamos alguna forma de reemplazar los marcadores de posición con datos reales y adjuntar lo que sucedió al DOM. En nuestro caso, este problema se resuelve usando micro-plantillas.

El siguiente código muestra los elementos en la interfaz de usuario a medida que se agregan.

 function addItemToPage(item) { if (document.getElementById(item._id)) return; let template = document.querySelector("#item-row").innerHTML; template = template.replace("{{name}}", item.name); template = template.replace("{{cost}}", item.cost); template = template.replace("{{quantity}}", item.quantity); template = template.replace("{{subTotal}}", item.subTotal); template = template.replace("{{row-id}}", item._id); template = template.replace("{{item-id}}", item._id); document.getElementById("item-table").tBodies[0].innerHTML += template; let totalCost = Number.parseFloat(   document.getElementById("total-cost").value ); document.getElementById("total-cost").value = totalCost + item.subTotal; } hoodie.store.withIdPrefix("item").on("add", addItemToPage); 

En este fragmento de código, la plantilla se obtiene del DOM, los marcadores de posición se reemplazan con datos reales y el resultado se adjunta al DOM. Luego se calcula el indicador de total-cost , la cantidad total para todos los elementos de la lista, que también se muestra en la interfaz.

▍Vue


Al cambiar a Vue, la plantilla utilizada por el script se eliminó y los elementos de la tabla se rediseñaron para usar la directiva Vue v-for , que le permite recorrer una propiedad que contiene los datos de los elementos. Así es como se ve el marcado correspondiente.

 <div class="mdl-grid center-items"> <table id="item-table" class="mdl-data-table mdl-js-data-table mdl-shadow--2dp">   <thead>     <tr>       <th class="mdl-data-table__cell--non-numeric">Item Name</th>       <th class="mdl-data-table__cell--non-numeric">Cost</th>       <th class="mdl-data-table__cell--non-numeric">Quantity</th>       <th class="mdl-data-table__cell">Sub-total</th>       <th class="mdl-data-table__cell--non-numeric">         <button class="mdl-button mdl-js-button mdl-button--icon">           <i class="material-icons">delete</font></i>         </button>       </th>     </tr>   </thead>   <tbody>     <tr v-for="item in items" :key="item._id">       <td class="mdl-data-table__cell--non-numeric">{{ item.name}}</td>       <td class="mdl-data-table__cell--non-numeric">{{ item.cost}}</td>       <td class="mdl-data-table__cell--non-numeric">{{ item.quantity}}</td>       <td class="mdl-data-table__cell">{{ item.subTotal}}</td>       <td class="mdl-data-table__cell--non-numeric">         <button @click="deleteRow(item._id)" class="mdl-button mdl-js-button mdl-button--icon mdl-button--colored">           <i class="material-icons">remove</font></i>         </button>       </td>     </tr>   </tbody> </table> </div> <div class="mdl-grid center-items"> <div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label">   <!-- <input class="mdl-textfield__input" type="number" id="total-cost" readonly value="0">   <label class="mdl-textfield__label" for="cost">Total Item Cost</label> -->   <h4>Total Cost: {{ total }}</h4> </div> </div> 

De hecho, no se han realizado cambios tan grandes en el marcado. Aquí, se copia el contenido de la micro-plantilla utilizada anteriormente y se utilizan las directivas y los mecanismos de interpolación del texto Vue. La directiva v-for se utiliza para enumerar los elementos cuyos datos se tomaron de la propiedad de items . Los datos se muestran en columnas utilizando las capacidades de la construcción Vue {{ item.name }} . Este mecanismo es similar a los marcadores de posición utilizados en la micro-plantilla. La cantidad total también se muestra en la página utilizando la tecnología de interpolación de texto.

Ahora finalizaremos el código JavaScript en el index-vue.js y obtendremos lo siguiente.

 const vm = new Vue({ el: "#app", data: {   name: "",   cost: "",   quantity: "",   items: [] }, computed: {   //     total: function() {     // `this`    vm     return this.items.reduce(       (accumulator, currentValue) => accumulator + currentValue.subTotal,       0     );   } }, methods: {   ..... } }); hoodie.store.withIdPrefix("item").on("add", item => vm.items.push(item)); 

Una variante del mecanismo descrito, preparada por Vue, resultó ser mucho más corta y simple que la creada con JS regular. En este código, se agregó la propiedad de datos de items , que se utiliza en la directiva v-for . Cuando se agrega un elemento, el Hoodie llama a una función que realiza la vm.items.push(item) para actualizar el estado de la aplicación, y esto, gracias al sistema de reactividad Vue, actualiza automáticamente la interfaz de usuario. Para calcular la cantidad total, no es necesario hacer referencia a los elementos DOM. Aquí usamos una propiedad calculada que procesa el conjunto de datos de items usando la función .reduce() . Ahora, gracias al sistema de reactividad Vue, la interfaz de usuario se actualiza cuando estos valores cambian. Lo bueno es que todo esto le permite al programador no preocuparse por trabajar con elementos DOM en el código. Como resultado, con la ayuda de un código más pequeño, resolvimos el mismo problema, que requería mucho más código en el caso de JS regular (probablemente, si en lugar de JS ordinario en esta situación se usaría la biblioteca jQuery, el gato también saldría con un tamaño más grande, que el que obtuviste al usar Vue).

Guardar elementos como una lista


▍JavaScript


Después de agregar elementos, debe guardarlos para su uso posterior, para que luego pueda agregar otras listas de elementos al sistema. La aplicación tiene un botón Save List , que se encarga de recopilar datos, guardarlos como un grupo de elementos utilizando las herramientas de Hoodie y que el usuario pueda agregar un nuevo conjunto de elementos al sistema.

Cuando se usa JavaScript normal, la función correspondiente está vinculada al onClick botón onClick . Aquí está el marcado.

 //index.html <div class="mdl-grid center-items"> <button class="mdl-button mdl-js-button mdl-button--raised mdl-button--colored" onclick="pageEvents.saveList()">   Save List </button> </div> 

Aquí está el código de función.

 //index.js function saveList() { let cost = 0; hoodie.store   .withIdPrefix("item")   .findAll()   .then(function(items) {     for (var item of items) {       cost += item.subTotal;     }     //      hoodie.store.withIdPrefix("list").add({       cost: cost,       items: items     });     //      hoodie.store       .withIdPrefix("item")       .remove(items)       .then(function() {         //          document.getElementById("item-table").tBodies[0].innerHTML = "";         //          var snackbarContainer = document.querySelector("#toast");         snackbarContainer.MaterialSnackbar.showSnackbar({           message: "List saved succesfully"         });       })       .catch(function(error) {         //             var snackbarContainer = document.querySelector("#toast");         snackbarContainer.MaterialSnackbar.showSnackbar({           message: error.message         });       });   }); } window.pageEvents = { deleteItem: deleteItem, saveList: saveList .... }; 

▍Vue


Cambiar a Vue no requerirá muchos cambios. Todavía necesitamos vincular el controlador al evento que se activa cuando se hace clic en el botón, además, debemos agregar el método, que es el controlador de este evento, a la propiedad de methods del objeto Vue cuando se inicializa.

Así es como se verá el marcado ahora.

 <div class="mdl-grid center-items"> <button @click="saveList" class="mdl-button mdl-js-button mdl-button--raised mdl-button--colored">   Save List </button> </div> 

La @click="saveList" es una abreviatura de v-on:click="saveList" . Este mecanismo se utiliza para escuchar eventos DOM. La misma función saveList , que se utilizó en la versión de la aplicación escrita en JS puro, se agrega a la propiedad de methods del objeto Vue.

Barra de navegación


▍JavaScript


Ahora que los elementos se pueden guardar como una lista, nos gustaría poder ver las listas guardadas anteriormente con datos sobre el valor total de los bienes contenidos en ellas. Esta información se mostrará en otra página, se verá como la que se muestra a continuación.


Datos sobre listas de productos creados previamente

El marcado de esta página está en el archivo public/history.html , el código JS correspondiente está en el archivo public/js/src/history.js . Esta página e index.html tienen algo en común. Esta es una barra de navegación ubicada en la parte superior de la ventana. Este panel contiene enlaces a diferentes páginas, enlaces de Login y Register que invocan formularios de diálogo para ingresar al sistema y registrarse en él. También hay un botón Signout sesión para cerrar sesión.
En la versión de la aplicación creada usando JS ordinario, tenía que duplicar el mismo marcado en ambas páginas. Así es como se ve el código HTML de la barra de navegación.

 <header class="mdl-layout__header">   <div class="mdl-layout__header-row">   <!-- Title -->   <span class="mdl-layout-title">Shopping List</span>   <!-- Add spacer, to align navigation to the right -->   <div class="mdl-layout-spacer"></div>   <!-- Navigation. We hide it in small screens. -->   <nav class="mdl-navigation mdl-layout--large-screen-only">       <a class="mdl-navigation__link" href="index.html">Home</a>       <a class="mdl-navigation__link" href="history.html">History</a>       <a onclick="pageEvents.showLogin()" style="cursor: pointer" class="mdl-navigation__link login">Login</a>       <a onclick="pageEvents.showRegister()" style="cursor: pointer" class="mdl-navigation__link register">Register</a>       <a onclick="pageEvents.signout()" style="cursor: pointer" class="mdl-navigation__link logout">Logout</a>   </nav>   </div> </header> <div class="mdl-layout__drawer">   <span class="mdl-layout-title">Shopping List</span>   <nav class="mdl-navigation">   <a class="mdl-navigation__link" href="index.html">Home</a>   <a class="mdl-navigation__link" href="history.html">History</a>   <a onclick="pageEvents.showLogin()" style="cursor: pointer" class="mdl-navigation__link login">Login</a>   <a onclick="pageEvents.showRegister()" style="cursor: pointer" class="mdl-navigation__link register">Register</a>   <a onclick="pageEvents.signout()" style="cursor: pointer" class="mdl-navigation__link logout">Logout</a>   </nav> </div> 

Después de analizar este código, notará que cuando hace clic en los enlaces Login , Register y Logout Login , se Logout sus funciones correspondientes. Los controladores de eventos para los elementos descritos en este marcado se definen en el archivo index.js .

 import * as shared from "shared.js"; .... shared.updateDOMLoginStatus(); window.pageEvents = { showLogin: shared.showLoginDialog, showRegister: shared.showRegisterDialog, signout: shared.signOut }; 

Las funciones responsables de trabajar con la barra de navegación se declaran en el archivo shared.js .

 //   let loginDialog = document.querySelector("#login-dialog"); dialogPolyfill.registerDialog(loginDialog); let registerDialog = document.querySelector("#register-dialog"); dialogPolyfill.registerDialog(registerDialog); let showLoginDialog = function() { loginDialog.showModal(); }; let showRegisterDialog = function() { registerDialog.showModal(); }; let showAnonymous = function() { document.getElementsByClassName("login")[0].style.display = "inline"; document.getElementsByClassName("login")[1].style.display = "inline"; document.getElementsByClassName("register")[0].style.display = "inline"; document.getElementsByClassName("register")[1].style.display = "inline"; document.getElementsByClassName("logout")[0].style.display = "none"; document.getElementsByClassName("logout")[1].style.display = "none"; }; let showLoggedIn = function() { document.getElementsByClassName("login")[0].style.display = "none"; document.getElementsByClassName("login")[1].style.display = "none"; document.getElementsByClassName("register")[0].style.display = "none"; document.getElementsByClassName("register")[1].style.display = "none"; document.getElementsByClassName("logout")[0].style.display = "inline"; document.getElementsByClassName("logout")[1].style.display = "inline"; }; let updateDOMLoginStatus = () => { hoodie.account.get("session").then(function(session) {   if (!session) {     //          showAnonymous();   } else if (session.invalid) {     //   ,          showAnonymous();   } else {     //         showLoggedIn();   } }); }; let signOut = function() { hoodie.account   .signOut()   .then(function() {     showAnonymous();     let snackbarContainer = document.querySelector("#toast");     snackbarContainer.MaterialSnackbar.showSnackbar({       message: "You logged out"     });     location.href = location.origin;   })   .catch(function() {     let snackbarContainer = document.querySelector("#toast");     snackbarContainer.MaterialSnackbar.showSnackbar({       message: "Could not logout"     });   }); }; export { signOut, showRegisterDialog, showLoginDialog, updateDOMLoginStatus }; 

Este código exporta las funciones que se usaron en index.js . Las showLoginDialog() y showRegisterDialog() muestran cuadros de diálogo modales, respectivamente, para iniciar sesión y registrarse con él. La función de showAnonymous() sesión signout() permite al usuario cerrar sesión y llama a la función showAnonymous() , que oculta el enlace Logout y muestra solo los enlaces Register e Login . La función updateDOMLoginStatus() verifica si el usuario está autenticado y muestra los enlaces apropiados. Esta función se llama cuando se carga la página.

Para tener la misma barra de navegación en dos páginas diferentes, tuvimos que duplicar el marcado, acceder a los elementos DOM y usar las funciones CSS para mostrar y ocultar los enlaces en el panel. Veamos una barra de navegación alternativa creada por Vue.

▍Vue


Muchas aplicaciones web tienen los mismos fragmentos utilizados en diferentes páginas. Por ejemplo, barras de navegación. Dichas entidades deben colocarse en contenedores o presentarse como componentes adecuados para su reutilización. Vue proporciona al desarrollador un motor de componentes que puede usarse para resolver problemas como el que estamos considerando. Los componentes Vue son autónomos y reutilizables.

Vue , shared-vue.js . Vue, . .

 Vue.component("navigation", { props: ["isLoggedIn", "toggleLoggedIn"], template: `<div>             <header class="mdl-layout__header">       <div class="mdl-layout__header-row">         <!-- Title -->         <span class="mdl-layout-title">Shopping List</span>         <!-- Add spacer, to align navigation to the right -->         <div class="mdl-layout-spacer"></div>         <!-- Navigation. We hide it in small screens. -->         <nav class="mdl-navigation mdl-layout--large-screen-only">           <a class="mdl-navigation__link" href="index.html">Home</a>           <a class="mdl-navigation__link" href="history.html">History</a>           <a v-show="!isLoggedIn" @click="showLogin" style="cursor: pointer" class="mdl-navigation__link login">Login</a>           <a v-show="!isLoggedIn" @click="showRegister" style="cursor: pointer" class="mdl-navigation__link register">Register</a>           <a v-show="isLoggedIn" @click="logout" style="cursor: pointer" class="mdl-navigation__link logout">Logout</a>         </nav>       </div>     </header>     <div class="mdl-layout__drawer">       <span class="mdl-layout-title">Shopping List</span>       <nav class="mdl-navigation">         <a class="mdl-navigation__link" href="index.html">Home</a>         <a class="mdl-navigation__link" href="history.html">History</a>         <a v-show="!isLoggedIn" @click="showLogin" style="cursor: pointer" class="mdl-navigation__link login">Login</a>         <a v-show="!isLoggedIn" @click="showRegister" style="cursor: pointer" class="mdl-navigation__link register">Register</a>         <a v-show="isLoggedIn" @click="logout" style="cursor: pointer" class="mdl-navigation__link logout">Logout</a>       </nav>     </div>           </div>`, methods: {   showLogin: function() {     const loginDialog = document.querySelector("#login-dialog");     dialogPolyfill.registerDialog(loginDialog);     loginDialog.showModal();   },   showRegister: function() {     const registerDialog = document.querySelector("#register-dialog");     dialogPolyfill.registerDialog(registerDialog);     registerDialog.showModal();   },   logout: function() {     hoodie.account       .signOut()       .then(() => {         this.toggleLoggedIn();       })       .catch(error => {         alert("Could not logout");       });   } } }); 

Vue, navigation , , , Vue. — props . props — . , , , props . isLoggedIn , .

, template , , . , , JS, , Vue — v-show @click . v-show . Logout , isLoggedIn true , falseLogin Register . , Vue v-if v-else . . @clickv-on:click . showLogin , showRegister logout .

methods . logout , , this.toggleLoggedIn() , props . , props , , isLoggedin , . , Vue DOM.
index.html . , 59-84. .

 <navigation v-bind:is-logged-in="isLoggedIn" v-bind:toggle-logged-in="toggleLoggedIn"></navigation> 

JS- isLoggedIn toggleLoggedIn , , props , , kebab-case. v-bind . isLoggedIn . : v-bind , .

 <navigation :is-logged-in="isLoggedIn" :toggle-logged-in="toggleLoggedIn"></navigation> 

isLoggedIn , toggleLoggedIn — , Vue index-vue.js . .

 const vm = new Vue({ el: "#app", data: {   name: "",   cost: "",   quantity: "",   items: [],   isLoggedIn: false }, computed: {   .....//   }, methods: {   toggleLoggedIn: function() {     this.isLoggedIn = !this.isLoggedIn;   },   ......//   } }); .....//   hoodie.account.get("session").then(function(session) { if (!session) {   //       vm.isLoggedIn = false; } else if (session.invalid) {   vm.isLoggedIn = false; } else {   //       vm.isLoggedIn = true; } }); 

Vue , , , , Vue. , Vue DOM , .


▍JavaScript


Login Register , . , , , . 171-244 index.html 100-158 history.html .

 <dialog id="login-dialog" class="mdl-dialog"> <h4 class="mdl-dialog__title">Login</h4> <div class="mdl-dialog__content">   <div class="mdl-grid center-items">     <!-- Simple Textfield -->     <div class="mdl-textfield mdl-js-textfield">       <input class="mdl-textfield__input" type="text" id="login-username">       <label class="mdl-textfield__label" for="login-username">Username</label>     </div>   </div>   <div class="mdl-grid center-items">     <!-- Simple Textfield -->     <div class="mdl-textfield mdl-js-textfield">       <input class="mdl-textfield__input" type="password" id="login-password">       <label class="mdl-textfield__label" for="login-password">Password</label>     </div>   </div>   <div class="mdl-grid center-items">     <!-- Simple Textfield -->     <div class="mdl-textfield mdl-js-textfield">       <span id="login-error"></span>     </div>   </div> </div> <div class="mdl-dialog__actions">   <button onclick="pageEvents.closeLogin()" type="button" class="mdl-button close">Cancel</button>   <button onclick="pageEvents.login()" type="button" class="mdl-button">Login</button> </div> </dialog> <dialog id="register-dialog" class="mdl-dialog"> <h4 class="mdl-dialog__title">Login</h4> <div class="mdl-dialog__content">   <div class="mdl-grid center-items">     <!-- Simple Textfield -->     <div class="mdl-textfield mdl-js-textfield">       <input class="mdl-textfield__input" type="text" id="register-username">       <label class="mdl-textfield__label" for="register-username">Username</label>     </div>   </div>   <div class="mdl-grid center-items">     <!-- Simple Textfield -->     <div class="mdl-textfield mdl-js-textfield">       <input class="mdl-textfield__input" type="password" id="register-password">       <label class="mdl-textfield__label" for="register-password">Password</label>     </div>   </div>   <div class="mdl-grid center-items">     <!-- Simple Textfield -->     <div class="mdl-textfield mdl-js-textfield">       <span id="register-error"></span>     </div>   </div> </div> <div class="mdl-dialog__actions">   <button onclick="pageEvents.closeRegister()" type="button" class="mdl-button close">Cancel</button>   <button onclick="pageEvents.register()" type="button" class="mdl-button">Register</button> </div> </dialog> 

, , shared.js index.js .

 //shared.js //   let loginDialog = document.querySelector("#login-dialog"); dialogPolyfill.registerDialog(loginDialog); let registerDialog = document.querySelector("#register-dialog"); dialogPolyfill.registerDialog(registerDialog); let closeLoginDialog = function() { loginDialog.close(); }; let closeRegisterDialog = function() { registerDialog.close(); }; let showAnonymous = function() { ... }; let showLoggedIn = function() { .... }; let signOut = function() { .... }; let updateDOMLoginStatus = () => { .... }; let login = function() { let username = document.querySelector("#login-username").value; let password = document.querySelector("#login-password").value; hoodie.account   .signIn({     username: username,     password: password   })   .then(function() {     showLoggedIn();     closeLoginDialog();     let snackbarContainer = document.querySelector("#toast");     snackbarContainer.MaterialSnackbar.showSnackbar({       message: "You logged in"     });   })   .catch(function(error) {     console.log(error);     document.querySelector("#login-error").innerHTML = error.message;   }); }; let register = function() { let username = document.querySelector("#register-username").value; let password = document.querySelector("#register-password").value; let options = { username: username, password: password }; hoodie.account   .signUp(options)   .then(function(account) {     return hoodie.account.signIn(options);   })   .then(account => {     showLoggedIn();     closeRegisterDialog();     return account;   })   .catch(function(error) {     console.log(error);     document.querySelector("#register-error").innerHTML = error.message;   }); }; export { register, login, closeRegisterDialog, closeLoginDialog, ... }; 

index.js.

 //index.js window.pageEvents = { closeLogin: shared.closeLoginDialog, showLogin: shared.showLoginDialog, closeRegister: shared.closeRegisterDialog, showRegister: shared.showRegisterDialog, login: shared.login, register: shared.register, signout: shared.signOut }; 

▍Vue


Vue . , .

 Vue.component("login-dialog", { data: function() {   return {     username: "",     password: ""   }; }, props: ["toggleLoggedIn"], template: `<dialog id="login-dialog" class="mdl-dialog">     <h4 class="mdl-dialog__title">Login</h4>     <div class="mdl-dialog__content">       <div class="mdl-grid center-items">         <!-- Simple Textfield -->         <div class="mdl-textfield mdl-js-textfield">           <input v-model="username" class="mdl-textfield__input" type="text" id="login-username">           <label class="mdl-textfield__label" for="login-username">Username</label>         </div>       </div>       <div class="mdl-grid center-items">         <!-- Simple Textfield -->         <div class="mdl-textfield mdl-js-textfield">           <input v-model="password" class="mdl-textfield__input" type="password" id="login-password">           <label class="mdl-textfield__label" for="login-password">Password</label>         </div>       </div>       <div class="mdl-grid center-items">         <!-- Simple Textfield -->         <div class="mdl-textfield mdl-js-textfield">           <span id="login-error"></span>         </div>       </div>     </div>     <div class="mdl-dialog__actions">       <button @click="closeLogin" type="button" class="mdl-button close">Cancel</button>       <button @click="login" type="button" class="mdl-button">Login</button>     </div>   </dialog>`, methods: {   closeLogin: function() {     const loginDialog = document.querySelector("#login-dialog");     dialogPolyfill.registerDialog(loginDialog);     loginDialog.close();   },   login: function(event) {     hoodie.account       .signIn({         username: this.username,         password: this.password       })       .then(() => {         this.toggleLoggedIn();         this.closeLogin();       })       .catch(error => {         console.log(error);         document.querySelector("#login-error").innerHTML = "Error loggin in";       });   } } }); 

data , props , template methods , Vue.component() .

, , Vue .

 //index.html <login-dialog v-bind:toggle-logged-in="toggleLoggedIn"></login-dialog> 

.

, , Vue. , , . , - Push API .

Resumen


-, JS, Vue.js. HTML, CSS JavaScript (jQuery), Vue . ES6 , .

Vue , , . , , , Vue. , , Vue .

, JS. — , Vue.

Estimados lectores! - ?

Source: https://habr.com/ru/post/es413311/


All Articles