PassionateDeveloper
PassionateDeveloper

Reputation: 15138

Attribute doesnt change inside ShadowDOM

In my following code a change of the value doesnt update the input value: Why?

class textfield extends HTMLElement {

    get value()
    {
        return this.getAttribute("value");
    }

    set value(val)
    {
        this.setAttribute('value', val);
    }

    get readonly() {
        return this.hasAttribute("readonly");
    }

    set readonly(val)
    {
        if (val)
        {
            this.setAttribute("readonly", '');
        }
        else
        {
            this.removeAttribute("readonly");
        }
    }

    static get observedAttributes() {
        return ['readonly', 'value'];
    }

    attributeChangedCallback(name, oldValue, newValue) {
        console.log(name, newValue) ;
    }

    constructor()
    {
        super();
        this.attachShadow({ "mode": "open" });
    }

    connectedCallback()
    {
        this.shadowRoot.innerHTML =  
        /*html*/`
        <div>
            <div>
                Lable
            </div>
            <div>
                <input id="vi-input" type="text" value="${this.getAttribute("value")}" ${ this.hasAttribute("readonly") ? "readonly" : "" } />
            </div>
            <div>
                <span>Error</span>
            </div>
        </div>
        `;
    }
}

window.customElements.define("vi-textfield", textfield);

And in my index.html:

.... 
        <vi-textfield id="myInput" value="2" validation="^\d{4}$" ></vi-textfield>
....

When I update it by JS, lets say by:

document.getElementById("myInput").value = 3;

there will be the given console.log of attributeChangedCallback but the value itself doesnt change....

Upvotes: 0

Views: 1693

Answers (2)

Your input doesn't update because they are 2 different elements.

 set value(val) {
   this.setAttribute('value', val);
 }

sets the value attribute on

<vi-textfield value="[val]">

not the value attribute on

<input id="vi-input" type="text" value="${this.getAttribute("value")}"/>

which is another element in your elements shadowDOM

value="${this.getAttribute("value")}" runs once, because connectedCallback() runs once

If you want to set both, you can do:

 set value(val) {
   this.setAttribute('value', val);
   this.shadowRoot.getElementById("vi-input").setAttribute("value", val);
 }

If you want them in sync, you also need setAttribute code for when value on the input changes.

PS.

set readonly(val) {
        if (val) {
            this.setAttribute("readonly", '');
        } else {
            this.removeAttribute("readonly");
        }
    }

can be written as:

set readonly(val) {
        this.toggleAttribute("readonly", val);
}

Upvotes: 3

Erik van de Ven
Erik van de Ven

Reputation: 4975

I guess you are setting the value, but not doing anything after that, at least not to the input field.. Your element vi-textfield actually is updated if you have a look at your elements tab using your browser's developer tools. You might be able to rebuild your html for example after setting the value by calling

this.connectedCallback();

To make sure the input field contains the updated value as well.
Or you need to find a way to select and update the input field separately. Or just recall the connectedCallback() method like I mentioned.

Or you could add some kind of MutationObserver to your class (https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver), but I don't think this should work out of the box what you might have thought: while updating the value of the host, update the shadow elements.

Upvotes: 0

Related Questions