Tras lo explicado en la entrada anterior sobre cómo crear componentes, damos un nuevo paso en componentes, y vamos a ir un paso más allá, separando lo que pasa en el login, de lo que pasa con el contenido una vez logueado. Para ello, crearemos dos componentes, que serán: sólo el login por un lado, y el contenido a mostrar por otro.
Este ejercicio planteado tiene varios aspectos técnicos de interés:
- Enviar datos desde el padre hasta el componente hijo con los v-bind
- La emisión de eventos desde un componente hijo a su padre
- Recepción de eventos en el padre (enviados por el hijo)
Componente de contenido
Empezamos por el componente de contenido, que recibe como dato el objeto user. Para recibir la propiedad user, utilizamos props. Una vez que se hace click en el botón de Cerrar, se emite un evento personalizado llamado logout, usando this.$emit(‘nombre_evento’, valores_enviar).
El código del componente es:
Vue.component('contenido', { props: ['user'], template: ` <div> <h1 class="jumbotron">Dentro {{ user.name}}</h1> <div class="container"> <h2>Contenido</h2> <button class="btn btn-default" @click="logout">Cerrar</button> </div> </div> `, methods: { logout: function() { this.$emit('logout'); } } })
Componente Login
El segundo de los componentes es login, que sí cambia bastante respecto a la versión previa. Al igual que el componente contenido, también recibe la propiedad user, pero además, necesitamos gestionar los errores (que se incluyen en data). Cuando el proceso de logueado es correcto, se emite un evento logged y se envia la información de user, que se ha modificado en el componente de login.
El código completo del componente es:
Vue.component('login', { props: ['user'], template: ` <div> <h1 class="jumbotron">Bienvenido</h1> <div class="login-errors-container container" v-if="errors.length !== 0"> <ul class="login-errors list-group"> <li v-for="error in errors" class="list-group-item list-group-item-danger"> {{ error }}</li> </ul> </div> <form class="login container" v-on:submit.prevent="login"> <div class="login-field"> <label for="username" class="form-label">Nombre de usuario</label> <input id="username" type="text" v-model="user.name" class="form-control"/> </div> <div class="login-field"> <label for="password" class="form-label">Contraseña</label> <input id="password" type="password" v-model="user.password" class="form-control"/> </div> <div class="login-field"> <button type="submit" v-bind:disabled="is_form_empty" class="btn btn-primary">Entrar</button> </div> </form> <div class="container">Entra con usuario 'testing' y clave 'manejando'</div> </div> `, computed: { is_form_empty: function () { return !(this.user.name && this.user.password); } }, methods: { login: function () { this.errors = []; if (this.user.name.length < 6) { this.errors.push('El nombre de usuario tiene que tener al menos 6 caracteres'); } if (this.user.password.length < 6) { this.errors.push('La contraseña tiene que tener al menos 6 caracteres'); } if (!this.errors.length) { if (this.user.name == "testing" && this.user.password == "manejando") { this.$emit('logged', this.user) } else { this.errors.push('Usuario o clave incorrecta'); }; } }, }, data: function () { return { errors: [], } } })
Instancia Vue
¿Y cómo se gestiona todo? Pues desde la instancia Vue principal, cuyo código es:
const app = new Vue({ el: '#app', data: { logged: false, user: { name: '', password: '' } }, methods: { loggin: function(user) { this.logged = !this.logged; } }, template: ` <div> <login @logged="loggin(user)" v-if="!logged" :user="user"></login> <contenido @logout="logged = !logged" v-if="logged" :user="user"></contenido> </div> ` });
En el template, hemos usado tanto login como contenido, cuya visualización es dependiente de logged, con la directiva v-if. El traspaso de user se hace con :user mientras que los eventos emitidos por los componentes hijos se recogen en el padre con @.
Espero que os sea de utilidad!!
Happy coding!