90 RX – Ottimizziamo la ricerca

Esiste in rxjs un operatore che permette di attendere un tot di tempo prima di mandare avanti il flusso del programma, debounceTime, crea un delay espresso in millisecondi. Importiamolo

const {filter, map, switchMap, debounceTime} = rxjs.operators;

ora lo invochiamo prima di fare la chiamata getBooks

debounceTime(1200),

a questo punto aumentiamo o diminuiamo il tempo effettuando dei test.

Ora dobbiamo fare in modo che se effettuiamo una nuova ricerca, venga pulito il DOM, senza accodare i risultati precedenti. Creiamo quindi una funzione per resecare i risultati che invocheremo poi prima della chiamata getBooks().

function cleanBooks() {
    const books = document.querySelector('#books');
    if (books) {
        books.innerHTML = '';
    }
}

Questa funzione seleziona l’elemento con ID books dal DOM e se non è vuoto inizializza la proprietà innerHTML a vuoto.

Richiamiamo la funzione cleanBooks dopo il debounceTime, utilizzando un operatore che non modifichi lo stream dei dati, il tap, che agisce solo sulla visualizzazione, importiamolo da rxjs e richiamiamo la funzione

tap( () => cleanBooks() ),

Non inseriamo nessun parametro in quanto non necessario e chiamiamo la cleanBooks ogni volta che viene fatta la ricerca nel campo, verificando che la ricerca precedente venga inizializzata.

Inseriamo il totale dei libri

Aggiungiamo una funzione che stampa il totale dei libri trovati dalla ricerca.

Intanto mettiamo un <h1> nell’html con uno <span> con id found

<main role="main">
    <div class="album py-5 bg-light">
        <div class="container">
            
            <h1>BOOKS FOUND<span id="found">0</span></h1>

            <div class="row" id="books">
            </div>
        </div>
    </div>
</main>

Se ricordiamo, nell’interface GoogleBook avevamo la proprietà totalItems, quindi quando effettuiamo la ricerca , possiamo inserire all’inizio del pipe della getBook() una funzione che dai dati di GoogleBook mi estragga la lunghezza degli items.

tap( (data:GoogleBook) => showTotal(data.items.length)),

Creiamo la funzione showTotal() che seleziona lo span ID found con querySelector, controlla se ci sono dei risultati e in caso affermativo modifica il contenuto con textContent. Da notare che ho dovuto inizializzare la proprietà textContent = ‘ ‘ uguale a vuoto, per poi concatenare il valore di total, altrimenti rxjs segnalerebbe un errore perché non conosce tale proprietà

function showTotal( total: number) {
    const found = document.querySelector('#found');
    if(found) {
        found.textContent = '' + total;
    }
}

Notiamo che nonostante totalItems restituisca più valori, ce ne vengono restituiti solo 10, andremo poi a settare questo parametro, ma prima inseriamo un pulsante di reset e l’attivazione del pulsante search al click .

Attiviamo il bottone search

Dobbiamo fare in modo che quando viene premuto il tasto search venga attivata un’ altra funzione, per esempio searchButtonClicked() oltre che alla searchBooks(), creiamola

searchBooks();
function searchButtonClicked() {
    const book:any = document.querySelector('#search');
    if(book) {
        getBooks(book.value).subscribe( (book:Book) => displayBook(book))
    }
}

andiamo ad accedere al DOM selezionando l’elemento HTML con ID search che andremo poi ad inserire. Tipizziamo book come tipo any per evitare la segnalazione di errore.

Se esiste book chiameremo getBooks() passandogli il value dell’elemento inserito nel form di ricerca. Avremo così restituito un observable di cui effettueremo la subscribe uguale all’altra funzione searchBooks().

Colleghiamo il click al search nell’html aggiungendo

onclick="searchButtonClicked()"

modifichiamo il type submit con button in modo da non far ricaricare la pagina

<form class="form-inline mt-2 mt-md-0">
    <input class="form-control mr-sm-2" type="text" placeholder="Search" id="search" aria-label="Search">
    <button onclick="searchButtonClicked()" class="btn btn-outline-success my-2 my-sm-0" type="button">Search</button>
</form>

Pulsante di pulizia

aggiungiamo un altro bottone con le stesse caratteristiche, magari cambiamo solo la classe per cambiare colore, e creiamo l’evento onclick

 <button onclick="document.querySelector('#search').value=''; cleanBooks()"
        class="btn btn-info my-2 my-sm-0" type="button">Clear</button>

ho messo uno spazio tra i 2 bottoni, con il codice HTML &nbsp.

Nell’ onclick ho selezionato l’id search e gli ho assegnato un value vuoto e ho lanciato la nostra funzione cleanBooks per pulire anche il DOM.