Vuejs para programadores jQuery. Select de selección múltiple. Menús. XIV

Vuejs para programadores jQuery.

Select de selección múltiple. Menús. XIV


Antes de empezar con los menús trataremos un ultimo select que no hemos visto: Select de selección múltiple.




Será bastante corto por sencillo y además miraremos unas (pocas) cositas nuevas.

Una vez más usaremos una lista externa.


Enlaces

Codesandbox: https://githubbox.com/Gonzalo2310/jQuery-Vuejs/blob/master/Select/Multiple/lista.js

GitHub: https://github.com/Gonzalo2310/jQuery-Vuejs/blob/master/Select/Multiple/lista.js


Vamos al código


Enlaces:

Codesandbox: https://githubbox.com/Gonzalo2310/jQuery-Vuejs/blob/master/Select/Multiple/jQuerySelectMultiple.html

GitHub: https://github.com/Gonzalo2310/jQuery-Vuejs/blob/master/Select/Multiple/jQuerySelectMultiple.html


<div class="container top20">

<div class="row">

<div class="col-6">

<div class="form-group">

<label for="exampleFormControlSelect2">Selección múltiple</label>

<select multiple class="form-control" id="exampleFormControlSelect2" onclick="mostrar()">

</select>

</div>

</div>

<div class="col-6">

    Elegidos:

<ul id="elegidos">

</ul>

</div>

</div>

</div>


No hay nada demasiado destacable. Un select que tiene un onclik, un id por supuesto y como algo nuevo el atributo múltiple. Indispensable para que el select permite selección múltiple.


Más abajo un UL vacío

Esta vez hacemos todo el trabajo en el JS:


const listSelect = jQuery('#exampleFormControlSelect2')

const elegidos = jQuery('#elegidos')

listSelect.empty()

listaTotal.every(item => listSelect.append(templateOption(item)))

Definimos las constantes (dos: Una el Select y la otra el UL), vaciamos el select (Paso innecesario en realidad porque lo tenemos vacío pero por reflejo siempre lo coloco antes de cambiar el contenido) y luego recorremos el array externo y rellenamos el Select.


Para ello usamos una función de template. Miremos las dos funciones template que hemos creado:


function templateOption({titulo, index}) {

return `<option value="${index}">${titulo} (${index})</option>`

}

function templateLI({titulo: texto}) {

return `<li>${texto}</li>`

}


El primero, que es el que usamos para el select, no tiene misterios. Usa la desestructuración como lo hemos hecho en los anteriores posts y devolvemos el elemento HTML con el formato.


El segundo trae una novedad en el parámetro. Repasemos la estructura de la lista que estamos trayendo:


const listaTotal = [

{

titulo: 'Albaricoque',

index: 201

}

...

Cuando usamos la desestructuración tomamos del objeto que recibimos los campos que necesitamos y, hasta ahora, lo usamos con el nombre que tiene en el objeto. Esto de por si no esa mal pero si por las razones que sea necesitamos asociarlo a un nombre diferente la forma de hacerlo es la siguiente:


function ({campo: variable})


Siendo campo el valor del objeto esperado y variable el nombre de la variable que guardara el contenido en la función. templateLI es un ejemplo de ello. Lo hice así como una muestra del uso para aprovechar este ejemplo sencillo.


Solo nos queda ver la función asociada al click del select:


function mostrar() {

const listOptions = jQuery('#exampleFormControlSelect2 option:selected')

elegidos.empty()

listOptions.each(function (){

const item = listaTotal.find(element => element.index===parseInt(jQuery(this).val()))

if (!item) {

return

}

elegidos.append(templateLI(item))

})

}


Primero recogemos los elementos elegidos. En jQuery se hace igual cuando es un select simple o múltiple. Variamos el <ul> y luego recorremos el resultado de los elementos recogidos.Para eso usamos la función each. Esta es una función de jQuery. Su semántica es:


each(function(){})


Y nos entrega cada elemento de forma individual que podemos identificar con this y para usarlo con jQuery es necesario hacer jQuery(this).

La primer linea es buscar el elemento de la lista externa cuyo index coincida con el val() del item actual.

Si tenemos resultado agregamos al <ul> el elemento con el formato de nuestra función templateLI.

Y listo


Vayamos a Vuejs.


Enlaces

Codesandbox: https://githubbox.com/Gonzalo2310/jQuery-Vuejs/blob/master/Select/Multiple/VueJsSelectMultiple.html

GitHub: https://github.com/Gonzalo2310/jQuery-Vuejs/blob/master/Select/Multiple/VueJsSelectMultiple.html


HTML


<div class="container" id="app">

<div class="row">

<div class="col-6">

<div class="form-group">

<label for="exampleFormControlSelect2">Selección múltiple</label>

<select multiple class="form-control" id="exampleFormControlSelect2" v-model="selectLista">

<option v-for="item in listaTotal" :key="item.index" :value="item.index">{{item.titulo}}</option>

</select>

</div>

</div>

<div class="col-6">

Elegidos:

<ul id="elegidos">

<li v-for="item in listaForm" :key="item.index+100">{{item.titulo}}</li>

</ul>

</div>

</div>

</div>


Y ponemos ya el código y comentamos todo:


<script>

new Vue({

el: '#app',

data: {

selectLista: [],

listaTotal

},

computed: {

listaForm() {

let response = []

this.selectLista.forEach(item => {

let search = this.listaTotal.find(element => element.index === parseInt(item))

if (search) {

response.push(search)

}

})

return response

}

}

})

</script>


Hay un cambio en la definición de variables. listaTotal no define el tipo. En realidad es un atajo de JS.

Lo primero recordar que nuestra lista externa se llama listaTotal. En vuejs debemos definir una variable, métodos, computada, etc. que apunte a ella para poder usarlo en el HTML.

En este caso es una variable así que la podríamos definir con el mismo nombre así:


listaTotal: listaTotal


Para estos casos Js te permite ahorrarlo poniendo solo una vez el nombre y asume que se usa esa semántica. Así que:


listaTotal: listaTotal es lo mismo que listaTotal para la definición.


Es lo que estábamos usando en la desestructuración hasta ahora. Al hacer:


const item = {

titulo: 'Gonzalo',

index: 234

}


function titulos({titulo}) {}


para luego usarla así:


titulos(item)


En la definición de la función ({titulo}) en realidad estamos haciendo ({titulo: titulo}). Para cambiar el nombre que usaremos para referenciar a cada campo de una desestructuración tenemos el ejemplo que vimos anteriormente en este post más arriba.


Hay otro cambio. En los v-model de los selects hasta ahora estábamos usando un string que apuntaba al value pero esta vez usaremos un array. Si. Es así de simple. Si el select es múltiple basta con usar una variable que es un array para tener la lista de elegidos.


Ahora miraremos la computada que usamos en los <li>


listaForm() {

let response = []

this.selectLista.forEach(item => {

let search = this.listaTotal.find(element => element.index === parseInt(item))

if (search) {

response.push(search)

}

})

return response

}


Definimos un array vacío. Recorremos el array que usamos en el select (Que contiene los value), realizamos el mismo find que en jQuery y si hay elemento lo guardamos en el array local. Devolvemos el array local.


Como es una computada, cada cambio en el select modificara el array asociado, y eso hará que los resultados devueltos cambien automáticamente.


Listo. Con esto ya tenemos el uso de select múltiples en vuejs.

Enlaces proyecto total:

Codesandbox: https://githubbox.com/Gonzalo2310/jQuery-Vuejs/blob/master/Select/Multiple/indexSelectMultiple.html

GitHub: https://github.com/Gonzalo2310/jQuery-Vuejs/blob/master/Select/Multiple/indexSelectMultiple.html


Vayamos a los menús

Usaremos el formato de menús de Bootstrap pero este punto es uno de los que más difieren los diferentes frameworks CSS por lo que si se usa otro diferente mirar con atención la documentación relacionada.




El HTML de los menús suele ser complejo y largo. Esta vez no es la excepción así que presentaremos el HTML de a poco. Primero los enlaces:


Enlaces.

Codesandbox: https://githubbox.com/Gonzalo2310/jQuery-Vuejs/blob/master/Navs/Menus/jQueryNavBarMenu.html

GitHub: https://github.com/Gonzalo2310/jQuery-Vuejs/blob/master/Navs/Menus/jQueryNavBarMenu.html


Tenemos opciones simples:


<li class="nav-item ">

<a class="nav-link option active" href="#" title="home">Home <span class="sr-only">(current)</span></a>

</li>

La clase opción es obligatoria para este proyecto porque será como pillaremos todas las opciones en JS. title también es un atributo necesario que relaciona la opción con su contenido. y active es una clase de Bootstrap que define la estética del elemento activo en ese momento.


Los contenidos:


<div id="content-home" class="content-item active">

<h3>Home</h3>

<blockquote>

....

</blockquote>

</div>


Vemos que el id es "content-" más el title de la opción del menú relacionada. Y que también aquí usaremos la clase active con las opciones elegidas. Importante que todas tengan la clase content-item


Tenemos submenús:


<li class="nav-item dropdown">

<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">

Dropdown

</a>

<div class="dropdown-menu" aria-labelledby="navbarDropdown">

<a class="dropdown-item option" href="#" title="accion">Acción</a>

<a class="dropdown-item option" href="#" title="otra">Otra acción</a>

<div class="dropdown-divider"></div>

<a class="dropdown-item option" href="#" title="relevante">Contenido relevante</a>

</div>

</li>


El <li> tiene una clase extra de Bootstrap llamada dropdown para definir que es un menú que abre otro menú.

Los <a> debajo del div también tienen el title y el option. Son 3 opciones con un separador entre la segunda y la tercera.


Veamos como resolvemos todo esto con las clases en JS:


const navItem = jQuery('.option')

navItem.on('click', actualiza)

Definimos una constante que apunta a todos los elementos con la clase option.

Y asociamos el click en cualquiera de ellos con la función actualiza.


Veamos esa única función:


function actualiza(evt) {

const target = jQuery(evt.target)

navItem.removeClass('active')

target.addClass('active')

const anterior = jQuery('.content-item.active')

const actual = jQuery('#content-' + target.attr('title'))

anterior.fadeOut( 300, function() {

anterior.removeClass('active')

actual.fadeIn(300, function() {

actual.addClass('active')

})

})

}


Lo primero que hacemos es identificar el elemento sobre el que se hizo click (evt.target), luego removemos la clase active de todas las opciones del menú (navItem) y le asignamos la clase al elemento sobre el que se hizo click.


Lo siguiente es buscar el contenido (content-item) que está activo (active) por eso se usa .content-item.active, porque se busca el contenido que tenga ambas clases.

Luego buscamos el nuevo contenido a mostrar. Buscamos el id con "content-" + el title del elemento sobre el que se hizo click.


Los siguientes pasos son funciones de efectos de jQuery:


fadeOut(tiempo, function(){}) Aplica opacidad a un contenido hasta hacerlo transparente del todo en el tiempo determinado.


fadeIn(tiempo, function(){}) Quita la opacidad a un contenido hasta mostrarlo completamente en un tiempo determinado.


Aplicamos opacidad al elemento activo actualmente con una demora de unos 300 milisegundos y al terminar le quitamos la clase active, y comenzamos el proceso inverso en el contenido que ahora queremos mostrar y al terminar le agregamos la clase active.


Listo. Hemos completado el proceso en jQuery. Vayamos a Vuejs:


Enlaces

Codesandbox: https://githubbox.com/Gonzalo2310/jQuery-Vuejs/blob/master/Navs/Menus/VueJsNavBarMenu.html

GitHub: https://github.com/Gonzalo2310/jQuery-Vuejs/blob/master/Navs/Menus/VueJsNavBarMenu.html


También iremos mirando el HTML en partes:


<ul class="navbar-nav mr-auto">

<li class="nav-item ">

<a class="nav-link option" :class="{active: option==='home'}" href="#" @click="option='home'">Home <span class="sr-only">(current)</span></a>

</li>

</ul>


Para definir la clase active usamos el valor de una variable llamada option. Si es igual a determinado valor (el mismo que usabamos en el atributo title en jQuery) entonces se le asigna la clase. El evento es directo. Si se pulsa click se le asigna el valor a la variable.


Tenemos un cambio aquí:


<li class="nav-item dropdown" :class="{show: dropdownShow}">

<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" @click="dropdownShow=!dropdownShow">

Dropdown

</a>

<div class="dropdown-menu" aria-labelledby="navbarDropdown" :class="{show: dropdownShow}">


Mostrar o no el submenú dependerá de un boolean (dropdownShow) que cambiara de estado cuando se pulse en el menú. O sea que se abría si está cerrado y si volvemos a pulsar se cerrar.


También en el Js tenemos un watch:


watch: {

option: function () {

this.dropdownShow = false

}

}


Sobre la variable option. Cada vez que se pulse en una opción del menú el dropdown se cerrara


Y en lugar de un div contenedor de todas las opciones tenemos algo nuevo:


<transition-group name="list" tag="div" mode="out-in">


Transition es una característica de Vue para realizar efectos visuales entre elementos HTML. Usa dos maneras de trabajar. Con CSS y con hook JS. Aquí usaremos el CSS. Si quieres saber mas de transition: https://es.vuejs.org/v2/guide/transitions.html


Transition se divide en dos formas: entre dos elementos <transition> y entre varios elementos <transition-group> que es la que usaremos en este post.


Vamos a mirar los atributos:


mode: Indica la prioridad de la transición. out-in: Primero el que sale, luego el que entra. in-out: Primero el que entra y después el que sale.


tag: indica que elemento HTML es sustituido. En este caso es div pero si fuera una lista ul-li lo lógico seria que ocupara el lugar del ul por eso es importante este atributo.


name: indica el slug para las clases css. Las clases que podemos crear para los eventos son varios según el momento de la transición que queramos afectar. La lista completa es:

  • enter
  • enter-active
  • enter-to
  • leave
  • leave-active
  • leave-to

Y se usaría en este caso list-enter, lista-enter-active, etc.

Este grafico muestra una idea aproximada de donde se ubica cada evento:


En este proyecto el css relacionado es:


.list-enter-active {

transition: all 0.5s;

transition-delay: 0.6s;

}

.list-leave-active {

transition: all 0.5s;

}

.list-enter, .list-leave-to {

opacity: 0;

}


Marcamos cuando entre un elemento y cuando sale como opacidad 0


La salida del elemento se ha puesto una transacción que tarde 0.5 segundos.

Al entrada del siguiente es el mismo tiempo con un retraso (transition-delay) de una milésima de segundo mas que lo tarde en salir el item


El uso aquí es muy sencillo y es solo por presentar una alternativa al fadeIn fadeOut de jQuery. Tanto con transition de vuejs como con los keyframe y transitions de CSS se pueden lograr muchos efectos.


Si desea saber más de transitions de CSS: https://learn.shayhowe.com/advanced-html-css/transitions-animations/


Los contenidos HTML los tenemos así:


<div id="content-home" class="content-item" key="1" v-if="option==='home'">

<h3>Home</h3>

<blockquote>

....

</blockquote>

</div>


El mismo id, la misma clase pero aquí no usamos active sino que v-if cuando la variable sea igual al valor. En este caso 'home'.

¿Que es key? Key tiene que ser un valor único que identifique cada elemento que va a participar en las transiciones. En este caso y por ser pocas hemos usado una string de números seguidos.


Ahora repasemos el código JS:


<script>

new Vue({

el: '#app',

data: {

option: 'home',

dropdownShow: false

},

watch: {

option: function () {

this.dropdownShow = false

}

}

})

</script>


Definimos la variable 'option' para tener el valor de la opción elegida en el menú. El dropdown como boolean para controlar si el menú descendente está abierto o cerrado y el watch que antes cualquier cambio de opción de menú cierra el menú desplegable.


Y otra vez llegamos al final. Espero que halla sido de ayuda y educativo.


Consultas, dudas, comentarios: Slack de PEUM o en Twitter.



No hay comentarios:

Publicar un comentario

Vuejs para programadores jQuery. Tablas simple. Paginación. XV

Vuejs para programadores jQuery. Tablas simple. Paginación. XV Hoy veremos el código de una tabla sencilla con paginación y modal para ver ...