Sviluppando applicazioni che fanno uso intensivo di componenti, potrebbe capitare che il template non sia sufficente per rappresentare perfettamente le nostre esigenze, poiché alcuni aspetti di esso potrebbero essere dinamici in base all'istanza di componente. Immaginiamo per esempio un componente per creare offerte di prodotti all'interno di una piattaforma di e-commerce: la <product-card>
. Non è però possibile sapere a priori il contenuto di ciascun prodotto, quindi il template della product-card non potrà essere statico, bensì dovrà ospitare del markup personalizzato.
Per risolvere questo impasse, Vue mette a disposizione gli slot, ovvero delle porzioni di template che possono essere configurate dall'esterno di un componente e che vengono mixate al template originale. Riprendiamo il caso iniziale e guardiamo subito un esempio:
Vue.component('product-card', {
template: '#product-card-template'
});
new Vue({
el: '#vue-app'
});
<div id="vue-app">
<product-card>
Io sono il prodotto 1 e costo <strong>10€</strong>
</product-card>
<product-card>
Io sono il prodotto 1, sono <span class="offer">in offerta</span> e costo <strong>5€</strong>
</product-card>
</div>
<script type="text/x-template" id="product-card-template">
<div class='product-card' style="border:1px solid #ddd; margin: 10px; padding: 10px;">
<div><strong>Scheda prodotto</strong></div>
<slot></slot>
</div>
</script>
Partendo dal codice JS, notiamo la creazione di un componente product-card
che utilizza come template il contenuto del nodo #product-card-template
. Nell'HTML, invece, vediamo la creazione di due istanze del componente e il relativo template (con degli stili CSS inline). I principali aspetti da notare solo la presenza di contenuto personalizzato all'interno dei componenti <product-card>
e l'utilizzo del nodo <slot>
all'interno del template. Grazie a questo nuovo nodo, Vue inietta il contenuto presente nel componente all'interno del template permettendo di avere contenuto dinamico. Una volta elaborata la pagina, l'HTML che verrà prodotto sarà il seguente:
<div id="vue-app">
<div class="product-card" style="border: 1px solid rgb(221, 221, 221); margin: 10px; padding: 10px;">
<div><strong>Scheda prodotto</strong></div>
Io sono il prodotto 1 e costo <strong>10€</strong>
</div>
<div class="product-card" style="border: 1px solid rgb(221, 221, 221); margin: 10px; padding: 10px;">
<div><strong>Scheda prodotto</strong></div>
Io sono il prodotto 1, sono <span class="offer">in offerta</span> e costo <strong>5€</strong>
</div>
</div>
Sebbene l'utilizzo degli slot aumenti notevolmente la personalizzazione dei componenti, può capitare che avere un unico punto di flessibilità sia limitato. Proviamo ad immaginare un componente più complesso che presenta diverse "zone" di contenuto; un unico slot risulterebbe un po' stretto. Riprendiamo l'esempio del product-card
:
<div id="vue-app">
<product-card>
<span slot="title">Titolo del prodotto 1</span>
Io sono il prodotto 1 e costo <strong>10€</strong>
<div slot="footer">Garanzia 12 mesi</div>
</product-card>
<product-card>
<span slot="title">Titolo del prodotto 2</span>
Io sono il prodotto 1, sono <span class="offer">in offerta</span> e costo <strong>5€</strong>
<div slot="footer">Nessuna garanzia per i prodotti in offerta</div>
</product-card>
</div>
<script type="text/x-template" id="product-card-template">
<div class='product-card' style='border:1px solid #ddd; margin: 10px; padding: 10px;'>
<h4><slot name='title'></slot></h4>
<slot></slot>
<footer style='font-size: 10px'><slot name='footer'></slot></footer>
</div>
</script>
Modificando l'HTML in questo modo, abbiamo creato diversi slot: oltre a quello di default ora è presente lo slot title
e lo slot footer
. Utilizzando poi l'attributo slot
, possiamo definire quale contenuto verrà iniettato negli slot definiti; possiamo quindi, oltre a personalizzare il contenuto della card, avere anche HTML personalizzato all'interno del titolo e del footer. In questo caso l'HTML generato sarà:
<div id="vue-app">
<div class="product-card" style="border: 1px solid rgb(221, 221, 221); margin: 10px; padding: 10px;">
<h4><span>Titolo del prodotto 1</span></h4>
Io sono il prodotto 1 e costo <strong>10€</strong>
<footer style="font-size: 10px;"><div>Garanzia 12 mesi</div></footer>
</div>
<div class="product-card" style="border: 1px solid rgb(221, 221, 221); margin: 10px; padding: 10px;">
<h4><span>Titolo del prodotto 2</span></h4>
Io sono il prodotto 1, sono <span class="offer">in offerta</span> e costo <strong>5€</strong>
<footer style="font-size: 10px;"><div>Nessuna garanzia per i prodotti in offerta</div></footer>
</div>
</div>