Vuejs para programadores jQuery.
Selects filtro X
Esta vez trabajaremos con un form input que nos permita filtrar el contenido de un select.
Este post está muy relacionado con el anterior y hay cosas que se explicaron en ese post y aquí se verán levemente. Si no se ha visto el anterior es mejor hacerle un repaso: https://comunidad.programaresunamierda.com/2020/06/vuejs-para-programadores-jquery-selects.html
A modo de facilitar la visualización pondremos una lista a cada lado del select. A la izquierda pondremos los elementos que cumplen el criterio (y que coinciden con el contenido del select) y a la derecha la lista de los elementos que NO cumplen el criterio de búsqueda.
Una vez más vamos a usar un Js externo para traer un array común a ambos códigos que contiene objetos de un solo nivel con dos campos: titulo e index.
Enlace:
- Codesandbox: https://githubbox.com/Gonzalo2310/jQuery-Vuejs/blob/master/Select/Filtro/lista.js
- GitHub: https://github.com/Gonzalo2310/jQuery-Vuejs/blob/master/Select/Filtro/lista.js
Pondremos una parte pero lo aconsejable es mirar el código y por eso dejamos el enlace arriba
const listaTotal = [
{
titulo: 'Albaricoque',
index: 201
},
{
titulo: 'Cereza',
index: 202
}
...
]
La lógica es: titulo es un campo de texto que será lo que se mostrara e index es un valor numérico único.
En el post anterior explicamos por qué lo hacemos así y la lógica de <script>
JQUERY
Vamos al código comencemos por los enlaces:
- Codesandbox: https://githubbox.com/Gonzalo2310/jQuery-Vuejs/blob/master/Select/Filtro/jQuerySelectFiltro.html
- GitHub: https://github.com/Gonzalo2310/jQuery-Vuejs/blob/master/Select/Filtro/jQuerySelectFiltro.html
Codigo HTML:
<div class="container top20">
<div class="row">
<div class="col-4">
<div><span>Elegidos</span></div>
<ul id="listElegidos"></ul>
</div>
<div class="col-4">
<form>
<div class="form-row align-items-center">
<div class="form-group">
<label for="exampleInput">JQuery input simple</label>
<input type="text" class="form-control" id="exampleInput" autocomplete="off">
<label class="mr-sm-2 sr-only" for="inlineFormCustomSelect">Preferencia</label>
<select class="custom-select mr-sm-2" id="inlineFormCustomSelect">
</select>
</div>
</div>
</form>
</div>
<div class="col-4">
<div><span>Negados</span></div>
<ul id="listNegados"></ul>
</div>
</div>
</div>
Aquí no tenemos muchas sorpresas. Tenemos dos <ul> vacíos con ids listElegidos y listNegados, un select vacío con id inlineFormCustomSelect y un Input que nos servirá para filtrar los contenidos con id exampleInput
Así que todo pasa en el código js:
const origen = jQuery('#inlineFormCustomSelect')
const inputFiltro = jQuery('#exampleInput')
const listElegidos = jQuery('#listElegidos')
const listNegados = jQuery('#listNegados')
inputFiltro.on('input', actualizar)
actualizar()
Estas primeras líneas son las que estamos usando típicamente en estos proyectos. Descripción de las constantes:
- origen: apunta al select.
- inputFiltro: apunta al form input.
- listElegidos: apunta al ul donde mostraremos los que cumplen el criterio
- listNegados: apunta al ul donde mostraremos los que NO cumplen el criterio
Luego asociamos el evento input del elemento form input a la función actualizar (si quieren repasar porque este evento mirar el post: https://comunidad.programaresunamierda.com/2020/06/vuejs-para-programadores-jquery-form.html)
Y por último lanzamos actualizar() por primera vez.
Continuemos. En el post anterior hablamos de funciones template. Aquí usaremos dos:
function templateOption({index, titulo}) {
return '<option value="' + index + '">' + titulo + '</option>'
}
Esta función es igual que en el post anterior pero en lugar de enviar titulo e index en los parámetros enviamos el objeto y usamos la desestructuración del objeto.
Hablemos de ello:
Lo primero que debemos observar en los parámetros ({index, titulo}) es que no es la forma tradicional. Esas llaves significan: "Del objeto que vendrá en este parámetro toma los campos index y titulo" y además ese mismo nombre es el que usa para la variable dentro de la función.
No se usó en el post anterior porque no había necesidad de saturar. Ya había cosas nuevas allí.
Si se quiere aprender más sobre desestructuración visitar este link: https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Operadores/Destructuring_assignment
La otra función template que hemos usado es similar:
function templateUlList({titulo}) {
return `<li>${titulo}</li>`
}
Una vez más usamos la desestructuración para tomar solamente el campo titulo del objeto que enviemos y usamos algo diferente en el return:
return `<li>${titulo}</li>`
Lo primero que debemos observar es que las comillas son especiales. Ni son las comillas simples ni las dobles usadas normalmente. Usando esas comillas podemos intercalar variables en cadenas de texto con el formato ${variable} y esta característica es de JS por lo que el signo de $ no tiene relación alguna con jQuery.
Teniendo los dos templates se ha creado otra función:
function templateProcessArray(items, param) {
let result = []
switch (param) {
case 'OPTION':
items.every(item => result.push(templateOption(item)))
break
case 'UL':
items.every(item => result.push(templateUlList(item)))
break
}
return result.join('')
}
La idea en esta función es recibir un array y devolver un string que contiene todos los elementos convertidos a <option> o <li> según sea la necesidad. ¿Por qué que recibe un array? Porque el filtro al final nos puede devolver 0, 1 o mas elementos pero siempre nos devolverá un array con los resultados.
El parámetro param puede tener dos valores: 'OPTION' o 'UL' para poder diferenciar si necesitamos usar un template u otro. Si param tiene cualquier otro valor devolverá un string nulo.
Los CASE prácticamente comparten código:
items.every(item => result.push(templateOption(item)))
items.every(item => result.push(templateUlList(item)))
Lo vimos en el post anterior con detalle. Usamos every para interaccionar con cada elemento del array y guardamos en result (con push) el resultado de las funciones template usando como referencia el item.
Al final volvemos a usar join() para devolver una cadena de texto.
La ultima función es la ya familiar actualizar:
function actualizar() {
let elegidos = listaTotal.filter(item => item.titulo.includes(inputFiltro.val()))
origen.empty().append(templateProcessArray(elegidos, 'OPTION'))
listElegidos.empty().append(templateProcessArray(elegidos, 'UL'))
listNegados.empty().append(templateProcessArray(listaTotal.filter(item => !item.titulo.includes(inputFiltro.val())), 'UL'))
}
Lo nuevo está en la primer linea: filter.
Ya conocemos find. find devuelve el primer elemento que cumple la condición, filter en cambio devuelve TODOS los elementos que cumplan la condición y siempre devolverá un array (vacío si no hay coincidencias)
Revisemos la condición recordando que listaTotal está definida en el script externo:
listaTotal.filter(item => item.titulo.includes(inputFiltro.val())) // en el post anterior hemos visto las funciones arrow.
Primero repasemos:
listaTotal.filter()
Indica que se usara una función para hacer un filtro y se enviara a esa función como parámetro cada item.
filter debe tener una resolución lógica (o sea TRUE o FALSE).
Veamos la condición que debe cumplir:
item.titulo.includes(inputFiltro.val())
Antes que nada explicaremos que es includes. includes busca una subcadena en una cadena y devuelve si la contiene o no (TRUE o FALSE). La subcadena que buscamos es inputFiltro.val() que es el contenido del form input y la cadena que debería contenerlo es item.titulo
filter devolverá un array con los item que sean TRUE a esa condición o un array vacío si no hay coincidencias
La siguiente linea trabaja con el select. Lo vacía (empty()) y lo rellena (append()) con el resultado de la función que vimos antes. El array del parámetro es el resultado del filter y el string 'OPTION' es para que la cadena que devuelva sea de los elementos parseados al elemento HTML <option>.
La siguiente linea es idéntica a la anterior con dos excepciones. Lo guarda en listElegidos (la lista de los elegidos) y el string es 'UL' es para que la cadena que devuelva sea de los elementos parseados al elemento HTML <li>.
La ultima linea la iremos desmontando:
listNegados.empty().append(templateProcessArray(listaTotal.filter(item => !item.titulo.includes(inputFiltro.val())), 'UL'))
El anterior resultado de filter lo guardamos en una variable para optimizar el trabajo sobre los arrays ya que luego los usamos en dos ocasiones, sin embargo en este caso sera de un unico uso:
listaTotal.filter(item => !item.titulo.includes(inputFiltro.val()))
Y si comparamos con el filter anterior la condición es exactamente la misma pero negada(!) o sea, el resultado contrario.
Lo demás se repite de la línea anterior con una excepción el resultado se agrega a listNegados, la lista que tiene los que NO cumplen la condición.
Y hemos terminado con jQuery. Pasemos a Vuejs
VUEJS
Enlaces:
- Codesandbox: https://githubbox.com/Gonzalo2310/jQuery-Vuejs/blob/master/Select/Filtro/VueJsSelectFiltro.html
- GitHub: https://github.com/Gonzalo2310/jQuery-Vuejs/blob/master/Select/Filtro/VueJsSelectFiltro.html
HTML:
<div class="container top20" id="app">
<div class="row">
<div class="col-4">
<div><span>Elegidos</span></div>
<ul>
<li v-for="elegido in listElegidos" :key="elegido.index">{{elegido.titulo}}</li>
</ul>
</div>
<div class="col-4">
<form>
<div class="form-row align-items-center">
<div class="form-group">
<label for="exampleInput">JQuery input simple</label>
<input type="text" class="form-control" id="exampleInput" autocomplete="off" v-model="inputFiltro">
<label class="mr-sm-2 sr-only" for="inlineFormCustomSelect">Preferencia</label>
<select class="custom-select mr-sm-2" id="inlineFormCustomSelect">
<option v-for="elegido in listElegidos" :ref="elegido.index" :value="elegido.index">{{elegido.titulo}}
</option>
</select>
</div>
</div>
</form>
</div>
<div class="col-4">
<div><span>Negados</span></div>
<ul>
<li v-for="elegido in listNegados" :key="elegido.index">{{elegido.titulo}}</li>
</ul>
</div>
</div>
</div>
Y una vez más vemos que las operaciones principales que hemos definido como templates en jQuery aquí sé realizan directamente sobre el HTML y para hablar con propiedad de la variable y las computadas traeremos ya el código JS
<script>
new Vue({
el: '#app',
data: {
inputFiltro: ''
},
computed: {
listElegidos() {
return listaTotal.filter(item => item.titulo.includes(this.inputFiltro))
},
listNegados() {
return listaTotal.filter(item => !item.titulo.includes(this.inputFiltro))
}
}
})
</script>
Solo usamos una variable que es la que tendrá el contenido del form input y por ser un framework reactivo se actualizara sola.
Las computadas las hemos visto y explicado en el post anterior con detalle, pero recordemos que son tratadas como variables por el sistema y se actualizan cuando las variables usadas en su definición cambian de valor.
Las dos computadas usan la misma lógica que los filtros de jQuery aprovechando el filter de JS. Igual que en jQuery tenemos dos filtrados. Uno busca la subcadena en la cadena (esta vez la subcadena es inputFiltro) y la otra es la negación de ese filtro, o sea las cadenas que NO tienen esa subcadena.
Ahora si repasemos el HTML en los elementos que tenemos código Vuejs:
<li v-for="elegido in listElegidos" :key="elegido.index">{{elegido.titulo}}</li>
<li v-for="elegido in listNegados" :key="elegido.index">{{elegido.titulo}}</li>
Estas son las listas de elegidos y de NO elegidos. Son casi iguales menos de donde salen los items. Mientras la primer lista usa ListElegidos la segunda usa ListNegacion. Ambas son las computadas que hemos visto anteriormente.
Seguimos usando v-for en su forma más tradicional (item in array) y esta vez hemos cambiado el nombre de la variable item elegida simplemente para que no parezca que los elementos tienen que llamarse obligatoriamente item. Usamos el campo index del item para darle un valor único al key y usamos el campo titulo del item para mostrar el nombre del elemento asociado.
Casi podría predecirse el option del select:
<option v-for="elegido in listElegidos" :ref="elegido.index" :value="elegido.index">{{elegido.titulo}}</option>
Prácticamente es idéntica a la lista de elegidos con la excepción de que los <option> tienen el atributo HTML value al cual le colocamos el valor del index del item.
Y lo tenemos. Sencillo en construcción y espero que poco a poco VueJs vaya siendo cada dia más familiar.
Enlaces del proyecto:
- Codesandbox: https://githubbox.com/Gonzalo2310/jQuery-Vuejs/blob/master/Select/Filtro/indexSelectFiltro.html
- GitHub: https://github.com/Gonzalo2310/jQuery-Vuejs/blob/master/Select/Filtro/indexSelectFiltro.html
Consultas, dudas, comentarios: Slack de PEUM o en Twitter.
No hay comentarios:
Publicar un comentario