Popular combo SELECT2 com milhares de linhas

Fala pessoal, tenho uma aplicação WEB em Node.JS onde ao entrar na rota de cadastro de ocorrências passo ao render da página um array que é uma consulta a uma tabela de cadastro de material que tem mais de 10.000 registros, este array eu uso para popular um combobox SELECT2, está tudo ok, porém meu problema é que como possui muitos registros, a página demora para carregar, o mesmoa contece quando clico no SELECT2, demora para processar, tem alguma forma diferente de eu popular o SELECT2, tipo usando paginação, não sei, por favor, podem me ajudar?

Inicialização do elemento Select2:

<!-- Inicialização do elemento Select2 -->
<script>
    $(function () {
        $('.select2').select2({
            theme: "bootstrap4",
        });
    });
</script>

Consulta:

//Consulta 
async doQuery(queryToDo) {
    let pro = new Promise((resolve, reject) => {
        let query = queryToDo;
        //Executando a consulta
        this.db.query(query, function(err, result) {
            if (err) throw err;
            resolve(result);
        });
    })
    //Retornando o resultado
    return pro.then((val) => {
        return val;
    })
}

//Materiais
async getMateriais() {
    let query = "SELECT * FROM tb_cadastro_material ORDER BY material ASC";
    return this.doQuery(query)
}

Rota que faz a consulta e envia para a página:

let materiais = await DBModel.getMateriais();

//Passa o conteúdo das variáveis para a página principal
res.render('./pageAdmin', {
    //Populando elementos
    DTMaterial: materiais,
    .
    .
    .

E enfim o meu SELECT2 sendo populado no front end:

<option value="">-- Selecione --</option>
<% DTMaterial.forEach((row, index) => { %>
      <option value="<%= row.material %>"><%= row.material %></option>
<% }) %>

Há uma outra forma de popular considerando que a origem dos dados possui milhares de registros, e sim, é necessário todos pois o usuário poderá selecionar qualquer um da lista.

Pelo que vi nesse link: https://select2.org/data-sources/ajax, o select2 tem autocomplete, ou seja, ele vai pesquisando na medida em que for digitando no campo. Talvez vc possa tentar implementar isso em vez de carregar tudo de uma vez.

1 curtida

Então @Lucas_Camara, eu tentei de todas as formas usar a documentação deste link que você passou, cara, confesso que não consegui adequar ao meu código, pois eu não recebo os dados de um URL, ai tentei substituir a URL pela variável DTMaterial que contem os dados e também não rolou.

Hmmm, na arquitetura do seu sistema, vc não consegue expor algum endpoint REST para retornar uma lista não?


Veja: https://adityasridhar.com/posts/how-to-use-nodejs-without-frameworks-and-external-libraries

Insisto nisso pq vai facilitar muito a sua vida nesse sistema.

Vou dar uma olhada e tentar, obrigado

1 curtida

Consegui…
Segue solução:

Primeiro você deve criar uma API que que traz o resultado de uma query no formato JSON, veja:

1-Cria a rota:

//Rotas para APIs diversas, pode abrir sem login e senha
const { apiMaterial, } = require('../dao/api/apis.js');
rotas.get('/apiMaterial', apiMaterial);

2-Cria o a consulta que será utilizada na rota da API

//Materiais
async getMateriais(pn) {
    let query = "SELECT * FROM tb_cadastro_material WHERE material LIKE '"+ pn +"%' ORDER BY material ASC";
    return this.doQuery(query)
}

3-Cria o arquivo DAO da rota da API:

//NPM Install
const fse = require('fs-extra');

//Importando arquivo site-model.js que possui a classe com as funções de consulta
const DB = require('./../listas/selects');
const FUNCOES = require('./../util/funcoes');
const mysql = require('mysql2');
const config = require('./../../database/config');
const conn = mysql.createPool(config);

module.exports = {
  apiMaterial: (req, res) => {
      let DBModel = new DB(conn);
      (async function () {
          //Variáveis que recebe o mês e(ou) ano escolhido para popular os gráficos
          let filtro_pn = req.query.q;

          //http://jdn001:3000/apiMaterial/?&[q]=92290
          let materiais = await DBModel.getMateriais(filtro_pn);
          return res.json(materiais);

      })();
  },
};

4 - No front-end este é o trecho do componente SELECT2 que é o campo que receberá a lista de itens:

<div class="col-sm-2">
    <div class="form-group">
        <label for="material_ADD" class="label">Material</label>
        <select class="form-control form-control-sm select2"
            style="width: 100%;" name="material_ADD" id="material_ADD"
            required>
        </select>
    </div>
</div>

5-E por fim o script que contém o trecho JQuery com o AJAX consumindo a api e populando o campo SELECT2 (material_ADD):

<script>
    $(document).ready(function () {
        let hostName = $(location).attr('hostname');
        let descricao;
        let custo_unitario;

        $("#material_ADD").select2({
            placeholder: '--Pesquise--',
            minimumInputLength: 3,

            ajax: {
                url: 'http://' + hostName + ':3000/apiMaterial', //Endereço da api
                dataType: 'json',
                type: "GET",
                delay: 250, //Tempo para iniciar a pesquisa durante digitação

                data: function (params) {
                    return {
                        q: params.term, //Valor que será capturado o campo e passado no parâmetro "q" da API (Ex.: http://jdm090:3000/apiMaterial/?&[q]=92290)
                    };
                },

                processResults: function (data) {
                    //Retorna o resultado da pesquisa e popula o select2
                    var res = data.map(function (item) {
                        descricao = item.descricao;
                        custo_unitario = item.valor;

                        return {
                            id: item.material, //value do select2 utilizado no insert/update
                            text: item.material, //texto do select2 utilizado no front-end
                        };
                    });
                    return {
                        results: res
                    };
                },
                cache: true
            },
        //Aqui ao selecionar um item no SELECT2 outros campos são populados baseado no item escolhido
        }).on('change', function () {
            document.getElementById('descricao_ADD').value = descricao;
            document.getElementById('valor_unitario_ADD').value = formatBDToDecimal(custo_unitario);
            document.getElementById('valor_diferenca_ADD').value = formatBDToDecimal((custo_unitario) * document.getElementById('diferenca_ADD').value);
        });
    });
</script>

Nota adicional:
Para popular o SELECT2 com dados já existentes daquele registro, ou seja, ao clicar no botão editar para realizar alterações:

$('#material_EDIT').select2('trigger', 'select', {data: {id: '<%= row.material %>', text: '<%= row.material %>'}});

Solução 100% funcional superando as expectativas

2 curtidas