88 RX – mostriamo i libri nel DOM

in questa parte andremo a mostrare i libri nella finestra del browser ricevendoli dalla nostra subscribe. Possiamo quindi eliminare il tap che ci serviva solo come controllo e mettere la sua funzione direttamente nella subscribe creando una funzione displayBook().

.subscribe( (book:Book) => displayBook(book) );

La funzione displayBook() riceverà in ingresso il libri di tipo interface Book

function displayBook(book: Book) {

}

La funzione man mano che arrivano i dati li dovrà inserire all’interno del DOM utilizzando il template di bootstrap che dinamicamente verrà generato ogni volta, prendendolo dalla index.html

<div class="col-md-4">
    <div class="card mb-4 shadow-sm">
        <svg class="bd-placeholder-img card-img-top" width="100%" height="225"
             xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMidYMid slice" focusable="false"
             role="img" aria-label="Placeholder: Thumbnail"><title>Placeholder</title>
            <rect width="100%" height="100%" fill="#55595c"/>
            <text x="50%" y="50%" fill="#eceeef" dy=".3em">Thumbnail</text>
        </svg>
        <div class="card-body">
            <p class="card-text">This is a wider card with supporting text below as a natural lead-in to
                additional content. This content is a little bit longer.</p>
            <div class="d-flex justify-content-between align-items-center">
                <div class="btn-group">
                    <button type="button" class="btn btn-sm btn-outline-secondary">View</button>
                    <button type="button" class="btn btn-sm btn-outline-secondary">Edit</button>
                </div>
                <small class="text-muted">9 mins</small>
            </div>
        </div>
    </div>
</div>

Lo inseriamo dentro la nostra funzione all’interno di una nuova costante bookTpl racchiuso tra apice incerto (backtick) e lo ripuliamo delle parti che non ci servono, come l’svg o la descrizione statica. L’svg lo sostituiamo con un tag <img> e utilizziamo la forma eseguita ${book.} per inserire nel template i dati dinamicamente

function displayBook(book: Book) {
    const bookTpl = `<div class="col-md-4">
                    <div class="card mb-4 shadow-sm">
                    <img src="${book.thumbnail}" title="${book.title}" alt="${book.title}">
                        <div class="card-body">
                            <p class="card-text">${book.title}</p>
                            <div class="d-flex justify-content-between align-items-center">
                                <div class="btn-group">
                                    <button type="button" class="btn btn-sm btn-outline-secondary">View</button>
                                    <button type="button" class="btn btn-sm btn-outline-secondary">Edit</button>
                                </div>
                                <small class="text-muted">9 mins</small>
                            </div>
                        </div>
                    </div>
                </div> `;
}

adesso facciamo l’append del nostro template selezionando l’id books del <div> con classe row, creando l’elemento <div class=”col-md-4″> che elimineremo dal nostro template

function displayBook(book: Book) {
    const bookTpl = `<div class="card mb-4 shadow-sm">
                    <img src="${book.thumbnail}" title="${book.title}" alt="${book.title}">
                        <div class="card-body">
                            <p class="card-text">${book.title}</p>
                            <div class="d-flex justify-content-between align-items-center">
                                <div class="btn-group">
                                    <button type="button" class="btn btn-sm btn-outline-secondary">View</button>
                                    <button type="button" class="btn btn-sm btn-outline-secondary">Edit</button>
                                </div>
                                <small class="text-muted">9 mins</small>
                            </div>
                        </div>
                    </div>`;
    const div = document.createElement('div');
    div.setAttribute('class','col-md-3');
    div.innerHTML = bookTpl;
    document.querySelector('#books').appendChild(div);
}

abbiamo inserito il col-md-3 per adattare meglio le immagini, e effettuato un controllo sui dati books. Da notare il metodo setAttribute() con cui abbiamo inserito la classe

const div = document.createElement('div');
div.setAttribute('class','col-md-3');
div.innerHTML = bookTpl;
const books = document.querySelector('#books');
if (books) {
    books.appendChild(div);
}

Ora andiamo ad aggiungere il titolo nel template e nel p sostituiamo con la descrizione o stringa vuota se la descrizione non ci fosse

<h5>${book.title}</h5>
<p class="card-text">${book.description || ''}</p>

Aggiungiamo una barra di ricerca

Sfruttando sempre bootstrap cerchiamo una ricerca con bottone e lo incolliamo nella nostra index.html al posto del <botton> nella navbar

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

per ottenere una barra di questo tipo spostando il logo sopra e cambiando la classe justify-content-end

<div class="navbar navbar-dark bg-dark shadow-sm">
    <a href="#" class="navbar-brand d-flex align-items-center">
        <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="none" stroke="currentColor"
             stroke-linecap="round" stroke-linejoin="round" stroke-width="2" aria-hidden="true" class="mr-2"
             viewBox="0 0 24 24" focusable="false">
            <path d="M23 19a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h4l2-3h6l2 3h4a2 2 0 0 1 2 2z"/>
            <circle cx="12" cy="13" r="4"/>
        </svg>
        <strong>Album</strong>
    </a>
    <div class="container d-flex justify-content-end">

        <form class="form-inline mt-2 mt-md-0">
            <input class="form-control mr-sm-2" type="text" placeholder="Search" aria-label="Search">
            <button class="btn btn-outline-success my-2 my-sm-0" type="submit">Search</button>
        </form>
    </div>
</div>

a volte si potrebbe generare un errore per il numero di chiamate a google fatte da anonimo, quindi aggiungiamo una regola nello switchMap così non viene generato l’errore, o l’ideale è passare l’api key di google books tramite url, ma per semplificare agiamo nel primo modo

switchMap( (data:GoogleBook) => from(data.items || [])),

ora è il momento di far funzionare la ricerca, facciamo in modo che dopo 3 lettere inserite venga inviata la query, iniziamo aggiungendo un id all’ <input> search

<input class="form-control mr-sm-2" type="text" placeholder="Search" id="search" aria-label="Search">

Creiamo una nuova funzione searchBooks() dove andiamo a selezionare il tag <input> con javaScript e inseriamo un if che se esiste (diverso da 0 quindi campo non vuoto), facciamo qualcosa

function sarchBooks() {
    const searchEle = document.querySelector('#search');
    if (searchEle) {
        
        getBooks('game of thrones');
    }
}

Usando rxjs, dobbiamo creare un observable che scaturisca l’evento, lo facciamo nella prossima lezione.