Vue.js mette a disposizione gli slot come una potente risorsa per facilitare la creazione di componenti riutilizzabili. In questo articolo, esploreremo il concetto degli slot in Vue e come utilizzarli per creare componenti flessibili e riutilizzabili.

Affronteremo i seguenti argomenti, fornendo esempi pratici su come applicare gli slot Vue nelle tue applicazioni:

  • Definizione del contenuto di fallback per gli slot Vue.js
  • Gestione di multiple e named slots
  • Utilizzo di nomi dinamici per gli slot in Vue.js
  • Sfruttare gli slot con ambito in Vue.js
  • Ottenere la riutilizzabilità nelle applicazioni Vue.js tramite gli slot
  • Utilizzo degli slot nei componenti renderless

Iniziamo!

Cosa sono gli slot in Vue.js?

In Vue.js, gli slot sono un modo per passare contenuti a un componente. Consentono di definire una sezione del template di un componente che può essere sovrascritta dal parent component dandogli il pieno controllo sul layout e sul contenuto del child component.

Quale problema risolvono gli slot Vue?

Gli slot Vue consentono a un componente di accettare contenuti dinamici, “slot content”, e di reindirizzarli in una posizione specifica all’interno del template del componente, “slot outlet”. Questa posizione è indicata dall’elemento <slot>.

L’elemento <slot> funge da segnaposto per il contenuto dello slot fornito dal parent component. Questo consente agli utenti di aggiungere contenuti personalizzati al layout di un componente Vue, rendendolo più flessibile e riutilizzabile. Questo permette agli utenti di personalizzare il layout di un eliminando la necessità di un template fisso.

Di seguito è riportato un esempio di come funzionano gli slot. Presta attenzione ai commenti nel codice per individuare dove si trova il contenuto dello slot e lo slot outlet.

Considera un componente <CustomButton> che accetta contenuti come questo:

<CustomButton>
  Hello all! <!-- slot content -->
</CustomButton>

Il modello del componente  <CustomButton> potrebbe assomigliare a questo:

<button class="custom-btn">
  <slot></slot> <!-- slot outlet -->
</button>

Il DOM renderizzato finale apparirà come questo che segue:

<button class="custom-btn">Hello all!</button>

Come mostrato nel codice sopra, qualsiasi contenuto fornito nel parent component <CustomButton> viene passato ai tag slot del child component. Il componente <CustomButton> fornisce al button il contenuto ed il suo stile personalizzato.

Come già accennato, gli slot non sono limitati al testo: possono anche includere più elementi e altri componenti. Ad esempio:

<CustomButton>
  <span style="color:blue">Add</span>
  <AwesomeIcon name="plus" />
</CustomButton>

Ecco un altro esempio di un simple component che utilizza uno slot:

<!-- MyComponent.vue -->
<template>
  <div>
    <h1>My Component</h1>
    <slot></slot>
  </div>
</template>

In questo esempio, abbiamo un simple component denominato MyComponent che visualizza un titolo e un elemento <slot>. L’elemento <slot> è il punto in cui il parent component può inserire contenuto. Ecco un esempio di come il parent component può utilizzare il child component:

<!-- ParentComponent.vue -->
<template>
  <div>
    <my-component>
      <p>This is some content that will be inserted into the slot of the child component.</p>
    </my-component>
  </div>
</template>

In questo esempio, l’elemento <p> verrà inserito nell’elemento <slot> del child component, producendo il seguente output:

My Component
This is some content that will be inserted into the slot of the child component.

Il codice completo sarebbe simile a questo:

<!-- MyComponent.vue -->
<template>
  <div>
    <h1>My Component</h1>
    <slot></slot>
  </div>
</template>
<script>
export default {
  name: 'MyComponent'
}
</script>


<!-- ParentComponent.vue -->
<template>
  <div>
    <my-component>
      <p>This is some content that will be inserted into the slot of the child component.</p>
    </my-component>
  </div>
</template>
<script>
import MyComponent from './MyComponent.vue';

export default {
  name: 'ParentComponent',
  components: {
    'my-component': MyComponent
  }
}
</script>

Ecco come apparirebbe l’output finale del codice sopra:

Output Of Code For Simple Component Using Slots In Vue Js Showing Title - My Component - With Body Text

Specifica del contenuto di fallback per gli slot Vue.js

Il termine “fallback content” indica ciò che viene mostrato in uno slot nel caso in cui nessun contenuto venga fornito per quel determinato slot. In Vue, il fallback content di uno slot viene specificato utilizzando il contenuto predefinito all’interno del tag <slot>.

Per esempio:

<!-- Parent Component -->
<template>
  <div>
    <header>
      <slot name="header">Default Header</slot>
    </header>
    <main>
      <slot>Default Main Content</slot>
    </main>
    <footer>
      <slot name="footer">Default Footer</slot>
    </footer>
  </div>
</template>

<!-- Child Component -->
<template>
  <layout>
    <!-- No header content provided -->
    <p>Main Content</p>
    <!-- No footer content provided -->
  </layout>
</template>

Nell’esempio sopra, il parent component ha definito il fallback content per ciascuno slot. Se il child component non fornisce un contenuto per gli slot dell’intestazione o del piè di pagina, verrà visualizzato il fallback content.

Lavorare con multiple e named slots

A volte, potresti voler avere multiple slot in un single component. Per fare ciò, puoi utilizzare i named slots.

I named slots consentono di assegnare un nome a uno slot e utilizzare quel nome nel parent component per indicare in quale slot deve essere inserito il contenuto. Ecco un esempio di un componente che utilizza multiple named slots:

<template>
  <div>
    <h1>My Component</h1>
    <slot name="header"></slot>
    <slot name="body"></slot>
    <slot name="footer"></slot>
  </div>
</template>

In questo esempio, il child component ha tre named slots: header, body e footer. Il parent component può quindi utilizzare questi nomi per indicare in quale slot deve essere inserito il contenuto, in questo modo:

<template>
  <div>
    <my-component>
      <template v-slot:header>
        <p>This is the header content</p>
      </template>
      <template v-slot:body>
        <p>This is the body content</p>
      </template>
      <template v-slot:footer>
        <p>This is the footer content</p>
      </template>
    </my-component>
  </div>
</template>

In questo esempio, il parent component utilizza la direttiva v-slot per indicare in quale slot deve essere inserito il contenuto. L’output dovrebbe essere simile al seguente:

Utilizzare nomi di slot dinamici in Vue.js

Vue ti consente di passare un valore dinamico come nome di uno slot. Questo è utile quando si vuoi scegliere dinamicamente quale contenuto di slot renderizzare in base a una condizione. Per farlo, puoi utilizzare la sintassi delle parentesi quadre per associare il nome dello slot a un valore dinamico. Ecco un esempio:

<template>
  <div>
    <h1>My Component</h1>
    <slot :name="currentSlot"></slot>
  </div>
</template>

<script>
export default {
  data() {
    return {
      currentSlot: 'header'
    }
  }
}
</script>

<template>
  <div>
    <my-component>
      <template v-slot:[currentSlot]>
        <p>This is the {{ currentSlot }} content</p>
      </template>
    </my-component>
  </div>
</template>

In questo esempio, la proprietà dati currentSlot viene utilizzata per associare il nome dello slot. Nel parent component, la direttiva v-slot viene utilizzata per definire il contenuto dello slot, con il nome dello slot all’interno di parentesi quadre e associato alla proprietà dati currentSlot.

Il risultato di questo codice sarebbe This is the header content se la proprietà dati currentSlot  fosse impostata su header. Se la proprietà dati currentSlot viene modificata in un valore diverso, come body o footer, il contenuto dello slot cambierebbe di conseguenza.

Consideriamo un esempio più complesso.

Supponiamo di avere un componente riutilizzabile, come una card, che deve visualizzare contenuti diversi in base al contesto in cui viene utilizzata, ad esempio un componente  Card che include un titolo e alcuni contenuti. Si potrebbe definire uno slot predefinito per il contenuto e utilizzare un nome di slot statico come questo:

<template>
  <div class="card">
    <div class="card-header">{{ title }}</div>
    <div class="card-body">
      <slot></slot>
    </div>
  </div>
</template>

<script>
export default {
  props: {
    title: String
  }
}
</script>

Tuttavia, potrebbe essere necessario avere diversi tipi di card con strutture diverse, come una card con un’immagine, una con un elenco e così via. Per fare ciò, si potrebbero utilizzare nomi di slot dinamici invece dello slot predefinito:

<template>
  <div class="card">
    <div class="card-header">{{ title }}</div>
    <div class="card-body">
      <slot :name="slotName"></slot>
    </div>
  </div>
</template>

<script>
export default {
  props: {
    title: String,
    slotName: {
      type: String,
      default: 'default'
    }
  }
}
</script>

Ora, quando utilizzi il componente Card , puoi assegnare dinamicamente il nome dello slot in base al tipo di card che si desidera visualizzare:

<template>
  <Card title="Card with Image">
    <template v-slot:[slotName]>
      <img src="image.jpg"/>
    </template>
  </Card>
//if the value passed to 'slotName' above is 'image', an image card is used, 
//generating the code below:

<template>
  <Card title="Card with Image">
    <template v-slot:image>
      <img src="image.jpg"/>
    </template>
  </Card>
//and if the value passed to 'slotName' is 'list', a list card is used.

  <Card title="Card with List">
    <template v-slot:list>
      <ul>
        <li>Item 1</li>
        <li>Item 2</li>
      </ul>
    </template>
  </Card>
</template>

Utilizzo degli slot con ambito in Vue.js

Gli slot con ambito permettono al child component di passare dati al parent component. Invece di passare il contenuto al child component, con gli slot con ambito, il child component può inviare dati al parent component che li userà per visualizzare il contenuto nello slot.

Ecco un esempio di un child component che utilizza uno slot con ambito:

<template>
  <div>
    <h1>My Component</h1>
    <slot :item="item"></slot>
  </div>
</template>

<script>
export default {
  props: {
    item: Object
  }
}
</script>

In questo esempio, il child component ha un singolo slot con ambito che si aspetta di ricevere un oggetto item. Ha anche una proprietà props che accetta un oggetto item.

Il parent component può quindi utilizzare dati passati dal child component per eseguire il rendering del contenuto nello slot:

<template>
  <div>
    <my-component v-for="item in items" :key="item.id" :item="item">
      <template v-slot="{ item }">
        <p>{{ item.name }}</p>
        <p>{{ item.description }}</p>
      </template>
    </my-component>
  </div>
</template>

<script>
export default {
  data() {
    return {
      items: [
        { id: 1, name: 'Item 1', description: 'This is the first item' },
        { id: 2, name: 'Item 2', description: 'This is the second item' },
        { id: 3, name: 'Item 3', description: 'This is the third item' }
      ]
    }
  }
}
</script>

Nel codice sopra, il parent component utilizza un ciclo v-for per scorrere su un array di elementi e li passa al child component tramite la prop. Inoltre, utilizza anche la direttiva v-for per accedere ai dati passati dal child component ed eseguire il rendering del contenuto nello slot.

Raggiungere la riusabilità nelle app Vue.js con slot

Uno dei principali vantaggi nell’utilizzare gli slot è che consentono di creare componenti altamente riutilizzabili. Il parent component può così controllare il layout e il contenuto del child component, facilitando il suo riutilizzo in diversi contesti senza dover modificare il codice.

Inoltre, puoi combinare gli slot con funzioni per ottenere un codice ancora più flessibile e riutilizzabile. Possiamo pensare agli slot come a segnaposto per funzioni, dove il parent component fornisce una funzione da eseguire nel child component.

Nel child component, possiamo definire uno slot che si aspetta di ricevere una funzione come prop. Quindi, possiamo quindi utilizzare questo function slot per eseguire la funzione fornita all’interno del child component. Per esempio:

<template>
  <div>
    <slot :myFunction="myFunction"></slot>
  </div>
</template>
<script>
export default {
  props: {
    myFunction: Function
  }
}
</script>

Nell’esempio precedente, il child component definisce uno slot che si aspetta di ricevere una funzione chiamata myFunction come prop.  Questa prop viene poi utilizzata nel template del child component per eseguire la funzione al suo interno.

Nel parent component, possiamo usare la direttiva v-slot per passare una funzione al function slot del child component:

<template>
  <div>
    <child-component>
      <template v-slot="{ myFunction }">
        <button @click="myFunction()">Execute Function</button>
      </template>
    </child-component>
  </div>
</template>
<script>
import ChildComponent from './ChildComponent.vue'

export default {
  components: {
    ChildComponent
  },
  data() {
    return {
      message: 'Hello, World!'
    }
  },
  methods: {
    myFunction() {
      alert(this.message)
    }
  }
}
</script>

Nel codice sopra, il parent component passa la funzione chiamata myFunction al function slot del child component utilizzando la direttiva v-slot. Il function slot del child component viene quindi destrutturato per ricevere la prop myFunction, e utilizzata nel template del parent component per eseguire la funzione.

Usando i function slots in questo modo, possiamo creare una logica più flessibile e riutilizzabile all’interno dei nostri componenti Vue. Ad esempio, possiamo utilizzarli per eseguire diverse funzioni a seconda del contesto in cui viene utilizzato il componente.

Utilizzo degli slot nei componenti renderless

I componenti renderless sono una tecnica potente in Vue che può aiutarci a creare una logica riutilizzabile senza il sovraccarico di creare un componente UI completo.

Un componente renderless è essenzialmente un componente che non esegue il rendering di alcun markup HTML, ma fornisce funzionalità che possono essere utilizzate da altri componenti. Usando gli slot, un componente renderless può passare dati e funzionalità ad altri componenti che poi li useranno per eseguire il rendering del contenuto.

Ecco un esempio di un componente renderless che fornisce uno slot per permettere al parent component di renderizzare un elenco di elementi:

<template>
  <slot :items="items"></slot>
</template>

<script>
export default {
  data() {
    return {
      items: []
    }
  },
  methods: {
    addItem(item) {
      this.items.push(item)
    },
    removeItem(item) {
      const index = this.items.indexOf(item)
      if (index !== -1) {
        this.items.splice(index, 1)
      }
    }
  }
}
</script>

Nel codice sopra, il componente renderless non esegue il rendering di alcun markup HTML, ma fornisce invece uno slot che passa un array di elementi al parent component. Quest’ultimo può quindi usare questo slot per eseguire il rendering dell’elenco di elementi nel modo che preferisce.

Ecco un esempio di un parent component che utilizza il componente renderless per renderizzare un elenco di elementi:

<template>
  <div>
    <my-list>
      <template v-slot="{ items }">
        <ul>
          <li v-for="item in items" :key="item">{{ item }}</li>
        </ul>
      </template>
    </my-list>
  </div>
</template>

<script>
import MyList from './MyList.vue'

export default {
  components: {
    MyList
  },
  mounted() {
    this.$refs.list.addItem('Item 1')
    this.$refs.list.addItem('Item 2')
    this.$refs.list.addItem('Item 3')
    this.$refs.list.removeItem('Item 2')
  }
}
</script> 

Qui, il parent component utilizza il componente renderless MyList per eseguire il rendering di un elenco di elementi. Il componente MyList viene passato alla direttiva v-slot, che destruttura l’item prop e lo utilizza per renderizzare un elenco non ordinato di elementi.

Il parent component quindi aggiunge e rimuove elementi dall’elenco utilizzando i metodi addItem e removeItem del componente MyList.

È possibile usare componenti renderless per incapsulare logiche complesse e renderle riutilizzabili tra diversi componenti della nostra applicazione. Fornendo slot per il parent component, possiamo creare componenti più flessibili e componibili che possono essere utilizzati in vari contesti.

Conclusione

In questo articolo, abbiamo trattato le basi degli slot Vue e come possiamo essere utilizzati per creare componenti riutilizzabili e flessibili. Abbiamo anche discusso l’uso dei multiple e named slots, slot con ambito e slot nei componenti renderless.

Comprendere il potenziale degli slot ci consente di portare lo sviluppo Vue.js a un livello superiore, creando componenti altamente riutilizzabili e flessibili.

 

Vi aspettiamo al prossimo workshop gratuito per parlarne dal vivo insieme a Denny Biasiolli!

Clicca qui per registrarti!

 

Non perderti, ogni mese, gli approfondimenti sulle ultime novità in campo digital! Se vuoi sapere di più, visita la sezione “Blog“ sulla nostra pagina!