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ć.