Kontynuacja lekcji poprzedniej, dalej drążymy temat reaktowności bez Reacta/innego frameworka frontendowego. Do dzieła!
Ok, przypomnijmy sobie poprzedni markup:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="./reactivevar.js"></script>
</head>
<body>
<div id="counter">
<reactive-variable
id="reactiveVar"
pref="Counter: "
value="0"> </reactive-variable>
<button id="incBtn">+</button>
<button id="decBtn">-</button>
<button id="resBtn">Reset</button>
</div>
<script src="./reactive2.js" defer></script>
</body>
</html>
Oraz poprzedni kod:
let cnt = {
value: 0,
id: 'reactiveVar'
};
const cntHandler = {
set(obj, prop, value){
if(prop !== 'value')
return Reflect.set(...arguments);
document.getElementById(obj.id)
.setAttribute('value', value);
return Reflect.set(...arguments);
}
}
let cntProxy = new Proxy(cnt, cntHandler);
console.log(cntProxy);
cntProxy.value++;
console.log(cntProxy);
let buttons = document.querySelectorAll("#counter button");
buttons.forEach(function(btn){
btn.addEventListener("click", function(e){
if(e.target.matches("#incBtn"))
return cntProxy.value++;
if(e.target.matches("#decBtn"))
return cntProxy.value--;
if(e.target.matches("#resBtn"))
return cntProxy.value = 0;
});
});
RactiveVar to też nasz web-component z poprzednich lekcji. Ok, teraz zmienimy markup i zobaczymy, czy zwykły input, niebędący naszym komponentem też idzie obsłużyć:
<div id="counter">
<reactive-variable
id="reactiveVar"
pref="Counter: "
value="0"> </reactive-variable>
<button id="incBtn">+</button>
<button id="decBtn">-</button>
<button id="resBtn">Reset</button>
<input type="number" name="numIpt" id="numIpt">
</div>
Nawet mu value nie nadaliśmy, ale da radę. Tylko jego id dopiszmy we właściwym miejscu:
let cnt = {
value: 0,
id: 'numIpt'
};
Oczywiście teraz popsuliśmy counter. Za to w inpucie (na wciśnięcie przycisków) zmienia się odpowiednio value. Możemy spróbować to naprawić, bo teraz jak poruszamy inputem, to nic się nie aktualizuje w jego value:
let ipt = document.querySelector("#numIpt");
ipt.addEventListener("change", function(e){
e.preventDefault();
cntProxy.value = e.target.value;
console.log("hi")
console.log(cntProxy.value)
});
Teraz value się aktualizuje, ale w DOMie (w zbadaj element), wartość inputu się nie aktualizuje, tak jakby nie miał value w observedAttributes mówiąc językiem web-componentów…
Cóż, próbowałem już wszystkiego, żeby zrobić 2 way data binding i wymusić re-redner:
- dodać i usunąć atrybut value
- dodać i usunąć atrybut disabled
- ustawić style.display na none i znowu na block
- i tak dalej i tym podobne
Także będę musiał o tym poczytać, ale nie jest to temat dzisiejszej lekcji, temat to subskrypcje. Także robimy readonly na naszym inpucie, a 2way data binding zajmiemy się w następnych lekcjach:
<input type="number" name="numIpt" id="numIpt" readonly>
Ok, ale o co chodzi z subskrypcjami? Cóż, o to chodzi, że jak zmieniliśmy id, to nasz counter przestał działać. Trzeba to naprawić:
Dodam, że skaczemy na reactiveVar z powrotem:
let cnt = {
value: 0,
id: 'reactiveVar',
update(){
document
.getElementById(this.id)
.setAttribute('value', this.value);
}
};
const cntHandler = {
set(obj, prop, value){
if(prop !== 'value')
return Reflect.set(...arguments);
obj.update();
return Reflect.set(...arguments);
}
}
Niby działa, ale to stara wartość. Jak jest 1 to pokazuje 0 i tak dalej…
let cnt = {
value: 0,
id: 'reactiveVar',
update(newVal){
document
.getElementById(this.id)
.setAttribute('value', newVal);
}
};
const cntHandler = {
set(obj, prop, value){
if(prop !== 'value')
return Reflect.set(...arguments);
obj.update(value);
return Reflect.set(...arguments);
}
}
Ok, teraz działa. Pora na subskrybentów…
let cnt = {
value: 0,
id: 'reactiveVar',
subscibedIds: ['numIpt'],
update(newVal){
document
.getElementById(this.id)
.setAttribute('value', newVal);
this.subscibedIds.forEach(function(id){
document
.getElementById(id)
.setAttribute('value', newVal);
});
}
};
const cntHandler = {
set(obj, prop, value){
if(prop !== 'value')
return Reflect.set(...arguments);
obj.update(value);
return Reflect.set(...arguments);
}
}
Teraz subskrybenci też są aktualizowani. Konkretną rzecz zrobiliśmy, będziemy to kontynuować.