Iniziamo a strutturare il nostro web component in modo da esporre proprietà che possono essere manipolate dall'esterno. Consideriamo, per questo scopo, il seguente codice:
const styleRules = `
.rating {
color: orange;
}`;
class MyRating extends HTMLElement {
constructor() {
super();
this.maxValue = 5;
this.value = 0;
}
connectedCallback() {
this.createComponent()
}
createComponent() {
//...
}
createStarList() {
let div = document.createElement("div");
let star;
for (let i = 1; i <= this.maxValue; i++) {
if (i <= this.value) {
star = this.createStar("★");
div.appendChild(span);
} else {
star = this.createStar("☆");
}
div.appendChild(star);
}
return div;
}
createStar(starCode) {
//...
}
}
customElements.define("my-rating", MyRating);
Come possiamo vedere, le modifiche apportate rispetto alla precedente versione riguardano l'introduzione delle proprietà maxValue
e value
nel costruttore con dei valori predefiniti, ed il loro utilizzo all'interno della funzione createStarList()
nel determinare il numero ed il tipo di stelle da visualizzare.
L'introduzione di queste proprietà fa in modo che il numero massimo di stelle ed il numero di stelle corrispondente alla valutazione siano parametriche, ma se proviamo ad impostare il valore di questi attributi sul markup della pagina HTML, come nel seguente esempio, scopriamo che non ha alcun effetto sul rendering del componente:
<my-rating id="myRatingComponent" maxValue="6" value="2"></my-rating>
Nemmeno se proviamo ad impostare il valore di queste proprietà tramite JavaScript otterremo un effetto sul numero di stelle visualizzato:
<html>
<head>
...
<script>
window.onload = function() {
let myRatingComponent = document.getElementById("myRatingComponent");
myRatingComponent.maxValue = 6;
myRatingComponent.value = 2;
}
</script>
</head>
<body>
...
</body>
</html>
Questo dipende dal fatto che per avere una corrispondenza tra le proprietà di un componente, gli attributi del markup e il comportamento del componente dobbiamo intervenire sul componente aggiungendo un po' di codice ad-hoc.
Prima di iniziare a vedere come connettere attributi e proprietà, diamo
qualche accenno ad alcune regole di naming che dobbiamo tenere in considerazione
quando sviluppiamo un web component.
I nomi degli attributi devono essere in minuscolo e devono corrispondere ai
nomi delle proprietà del componente. Se il nome della proprietà del
componente usa il camel case, cioè usa una combinazione di lettere
minuscole e maiuscole come in maxValue, ad esso corrisponderà un
attributo con nome max-value.
Naturalmente il naming da solo non è sufficiente a sincronizzare i valori
di attributi e proprietà del componente, ma è un requisito necessario.
Per fare in modo che l'assegnamento di valori alle proprietà del nostro
componente si traduca nella visualizzazione del numero di stelle
corrispondenti dobbiamo ricorrere ai getter e setter
standard di una classe JavaScript. Riscriviamo pertanto il nostro
componente come mostrato di seguito:
class MyRating extends HTMLElement {
constructor() {
super();
this._maxValue = 5;
this._value = 0;
}
connectedCallback() {
this.createComponent()
}
get maxValue() {
return this._maxValue;
}
set maxValue(val) {
this._maxValue = val;
this.setAttribute("max-value", val);
this.replaceStarList();
}
get value() {
return this._value;
}
set value(val) {
this._value = val;
this.setAttribute("value", val);
this.replaceStarList();
}
replaceStarList() {
let starList = this.createStarList();
this.children[1].remove();
this.appendChild(starList);
}
createComponent() {
...
}
createStarList() {
...
}
createStar(starCode) {
...
}
}
Possiamo vedere come le proprietà maxValue
e value
sono ora definite
tramite i qualificatori set e get ed i loro valori sono
memorizzati nelle proprietà di appoggio _maxValue
e _value
.
Quando si accede in lettura alle proprietà, viene eseguito il codice
associato ai rispettivi getter, che non fanno altro che restituire
il valore delle corrispondenti proprietà di appoggio.
Quando si accede alle proprietà in scrittura viene eseguito il codice dei setter. Entrambi i setter si occupano di assegnare il
valore alla rispettiva proprietà di appoggio e di chiamare il metodo replaceStarList()
, che rigenera l'elenco delle stelle in base ai
nuovi valori e sostituisce l'elenco precedente. Vediamo inoltre che i setter si occupano di assegnare i nuovi valori ai corrispondenti
attributi max-value
e value
tramite il metodo standard setAttribute()
. Questa operazione si rende necessaria per mantenere la consistenza tra proprietà ed attributi.
Con queste modifiche, il codice di inizializzazione del nostro componente,
che avevamo presentato prima e che riportiamo di seguito per comodità, avrà
l'effetto atteso, cioè farà in modo che venga visualizzato un totale di sei
stelle di cui due internamente colorate:
<html>
<head>
...
<script>
window.onload = function() {
let myRatingComponent = document.getElementById("myRatingComponent");
myRatingComponent.maxValue = 6;
myRatingComponent.value = 2;
}
</script>
</head>
<body>
...
</body>
</html>