Visualizzare libri da un file JSON

In questa lezione impararemo a visualizzare dei libri da un file JSON. Ricordiamo che la visualizzazione dei dati da un file JSON è un’operazione comune nello sviluppo di applicazioni web e mobili poiché i file JSON forniscono un modo efficace per organizzare e trasferire dati strutturati.

Visualizzare libri da un file JSON – esempio

Supponiamo dunque di avere un file json che contiene dei libri a piacere con i dati di autori, categoria, cover e primo anno di pubblicazione. Prendo l’immagine della cover dalla API gratuita openlibrary.

Chiaramente potete aggiungere tanti altri libri a piacere, io ho creato questo file di esempio:

[
    {
      "title": "Il Signore degli Anelli",
      "author": "J.R.R. Tolkien",
      "category": "Fantasy",
      "coverUrl": "https://covers.openlibrary.org/b/id/0013119612-M.jpg",
      "first_publish_year": 1954
    },
    {
      "title": "Orgoglio e pregiudizio",
      "author": "Jane Austen",
      "category": "Romanzo",
      "coverUrl": "https://covers.openlibrary.org/b/id/14552692-M.jpg",
      "first_publish_year": 1813
    },
    {
      "title": "Il fu Mattia Pascal",
      "author": "Luigi Pirandello",
      "category": "Romanzo",
      "coverUrl": "https://covers.openlibrary.org/b/id/0010362119-L.jpg",
      "first_publish_year": 1949
    },
    {
      "title": "Il Trono di Spade",
      "author": "George R.R. Martin",
      "category": "Fantasy",
      "coverUrl": "https://covers.openlibrary.org/b/id/9269962-M.jpg",
      "first_publish_year": 1996
    },
    {
      "title": "Harry Potter e la pietra filosofale",
      "author": "J.K. Rowling",
      "category": "Fantasy",
      "coverUrl": "https://covers.openlibrary.org/b/id/0012373652-M.jpg",
      "first_publish_year": 1997
    },

    {
      "title": "Don Chisciotte",
      "author": "Miguel de Cervantes",
      "category": "Classici",
      "coverUrl": "https://covers.openlibrary.org/b/id/7977593-L.jpg",
      "first_publish_year": 1905
    }
  ]
  

Ho inserito questo file per semplicità all’interno della cartella assets.

Componente per la visualizzazione dei libri – script

Nello script importiamo il file books.json che contiene i dati dei libri.

Nel blocco data() definiamo i dati del componente Vue. Nel nostro questo caso, ci sono gli attributi:

  • books: un array vuoto che verrà popolato con i dati dei libri.
  • loading: una variabile booleana che indica lo stato di caricamento dei dati.
  • selectedCategory: una stringa vuota che rappresenta la categoria selezionata dall’utente.

Poi nella computed inseriamo le proprietà

  • allCategories che restituisce un array di tutte le categorie presenti nei libri. Utilizza un Set per assicurarsi che le categorie siano uniche e quindi trasforma il set in un array utilizzando Array.from().
  • displayedBooks che filtra i libri in base alla categoria selezionata dall’utente (selectedCategory). Se nessuna categoria è stata selezionata, restituisce tutti i libri; altrimenti, restituisce solo i libri che corrispondono alla categoria selezionata.

Infine il metodo filterBooks() viene chiamato quando il componente viene creato (created()) per caricare i dati dei libri dal file JSON (booksData). Inoltre, imposta la variabile loading su true prima del caricamento e su false dopo aver completato il caricamento.

Ecco dunque il codice di esempio:

<script>
import booksData from '@/assets/books.json';

export default {
  data() {
    return {
      books: [],
      loading: true,
      selectedCategory: '',
    };
  },
  computed: {
    allCategories() {
      const categories = new Set();
      this.books.forEach(book => categories.add(book.category));
      return Array.from(categories);
    },
    displayedBooks() {
      if (!this.selectedCategory) {
        return this.books;
      }
      return this.books.filter(book => book.category === this.selectedCategory);
    },
  },
  methods: {
    filterBooks() {
      this.loading = true;
        this.books = booksData;
        this.loading = false;
    },
  },
  created() {
    this.filterBooks();
  },
};
</script>

Componente per la visualizzazione dei libri – template

Dopo ho creato il componente che interagisce con le API. In questa fase ho utilizzato un unico componente per semplificare le operazioni.

Il template HTML contiene la struttura dell’applicazione, con un’intestazione (<header>), un corpo principale (<main>) e un elenco di libri all’interno di un contenitore (<div class="container">). I libri sono rappresentati come card all’interno di un wrapper con classe card-wrapper.

Ho inserito una select con la direttiva v-model che stabilisce un legame bidirezionale tra la variabile selectedCategory nel modello Vue e il valore selezionato nell’elemento <select>. Ciò significa che quando si seleziona un’opzione dall’elemento <select>, il valore di selectedCategory viene automaticamente aggiornato per riflettere la selezione dell’utente, e viceversa.

Nella select ho inserito l’evento @change="filterBooks": Questo è un evento Vue che ascolta quando l’utente cambia la selezione nell’elemento <select>. Quando l’utente seleziona un’opzione diversa, viene chiamato il metodo filterBooks() definito nel componente Vue. Questo metodo si occupa di filtrare i libri in base alla categoria selezionata e di aggiornare l’elenco dei libri visualizzati.

All’interno dell’elemento <select>, ci sono due opzioni <option>. La prima opzione ha un valore vuoto "" e un testo “Tutte le categorie”. Questa opzione rappresenta la scelta di visualizzare tutti i libri senza alcun filtro di categoria. La seconda opzione è generata dinamicamente utilizzando v-for e rappresenta ciascuna categoria presente nell’array allCategories. Il testo di ciascuna opzione corrisponde al nome della categoria, mentre il valore è anche il nome della categoria stessa.

Nella sezione dedicata alla card dei libri, con un ciclo v-for di Vue che itera attraverso ciascun libro nell’array displayedBooks. Per ogni libro, viene creato un elemento <div> con la classe card, che rappresenta una card di un libro. L’attributo :key="book.title" viene utilizzato per garantire una corretta identificazione delle card durante il ciclo.

Ecco come ho realizzato il template:

<template>
  <div>
    <header>
      <div class="header-content">
        <h1>Libreria</h1>
        <select v-model="selectedCategory" @change="filterBooks">
          <option value="">Tutte le categorie</option>
          <option v-for="category in allCategories" :key="category" :value="category">{{ category }}</option>
        </select>
      </div>
    </header>
    <main>
    <div class="container">
     <div class="card-wrapper">
      <div v-for="book in displayedBooks" :key="book.title" class="card">
        <div class="card-cover">
          <img :src="book.coverUrl" alt="Cover">
        </div>
        <div class="card-details">
          <h2>{{ book.title }}</h2>
          <div class="book-info">
            <p><strong>Autore:</strong> {{ book.author }}</p>
            <p><strong>Categoria:</strong> {{ book.category }}</p>
            <p><strong>Anno di pubblicazione:</strong> {{ book.first_publish_year }}</p>
          </div>
        </div>
      </div>
    </div>
  </div>
</main>
</div>
</template>

Divisone dei componenti

Potremmo organizzare il codice in componenti separati. Creando ad esempio un HeaderComponent e un MainComponent inseriti in App.Vue. Utilizziamo props ed emit per tale scopo.

Ecco dunque come si presenta App.vue:

<template>
    <div>
      <HeaderComponent :selectedCategory="selectedCategory" :allCategories="allCategories" @categoryChanged="handleCategoryChange" />
      <MainComponent :displayedBooks="displayedBooks" />
    </div>
</template>
  
<script>
  import HeaderComponent from './sections/HeaderComponent.vue';
  import MainComponent from './sections/MainComponent.vue';
  import booksData from '@/assets/books.json';
  
  export default {
    data() {
      return {
        books: [],
        loading: true,
        selectedCategory: '',
      };
    },
    computed: {
      allCategories() {
        const categories = new Set();
        this.books.forEach(book => categories.add(book.category));
        return Array.from(categories);
      },
      displayedBooks() {
        if (!this.selectedCategory) {
          return this.books;
        }
        return this.books.filter(book => book.category === this.selectedCategory);
      },
    },
    methods: {
      handleCategoryChange(category) {
        this.selectedCategory = category;
      },
      filterBooks() {
        this.loading = true;
        this.books = booksData;
        this.loading = false;
      },
    },
    created() {
      this.filterBooks();
    },
    components: {
      HeaderComponent,
      MainComponent
    }
  };
</script> 

Dunque l’HeaderComponent si può sviluppare in questo modo:

<template>
    <header>
      <div class="header-content">
        <h1>Libreria</h1>
        <select :value="selectedCategory" @change="handleChange">
          <option value="">Tutte le categorie</option>
          <option v-for="category in allCategories" :key="category" :value="category">{{ category }}</option>
        </select>
      </div>
    </header>
  </template>
  
<script>
  export default {
    props: {
      selectedCategory: String,
      allCategories: Array
    },
    methods: {
      handleChange(event) {
        this.$emit('category-changed', event.target.value);
      }
    }
  };
</script>  

Ed infine il MainComponent:



<template>
    <main>
        <div class="container">
            <div class="card-wrapper">
                <div v-for="book in displayedBooks" :key="book.title" class="card">
                    <div class="card-cover">
                        <img :src="book.coverUrl" alt="Cover">
                    </div>
                    <div class="card-details">
                        <h2>{{ book.title }}</h2>
                        <div class="book-info">
                            <p><strong>Autore:</strong> {{ book.author }}</p>
                            <p><strong>Categoria:</strong> {{ book.category }}</p>
                            <p><strong>Anno di pubblicazione:</strong> {{ book.first_publish_year }}</p>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </main>
</template>
  
<script>
  export default {
    props: {
      displayedBooks: Array
    }
  };
</script>

Conclusioni

In questa lezione abbiamo visto come sviluppare un’applicazione per visualizzare dei libri da un file JSON, nella prossima lezione vedremo come interagire con API esterne.

Alcuni link utili

Tutorial JavaScript

Versione rivisitata del gioco dell’impiccato: divertiti ad indovinare le parole ed impara a sviluppare uno dei giochi più famosi.

Slot Machine: segui una guida passo passo per imparare a svilupparne una semplicissima slot machine da zero.

Quiz interattivo: un quiz che permette di testare le conoscenze su determinati argomenti.

Memoria del colore: una sfida divertente che mette alla prova la memoria visiva e la capacità di ricordare sequenze di colori sempre più complesse.

Memory Game: Un’avvincente sfida di concentrazione e me