1

It's hard to get data between components if they have a bit complex structure. Yes there's a plenty of state management libraries out there. But I wonder what if all I need to do is just to set a different value for a component that I can't reach from where I want to set it.

for example, I have a tabs component (there will be only 1). I also added a button that sets a different tab but I can't reach my-tabs from it.

so can't I just

class MyTabs extends LitElement {
    static properties = {
        active: {}
    }
    constructor() {
        super()
        this.active = 0
        window.tabs = this
    }
}

class MyButton extends LitElement {
    render(){
        return html`
            <button @click=${e => window.tabs.active = 2}>back to tab 2</button>
        `
    }
}

It has a rule tho - 1 instance at most && you can't get. only to set cuz:

  • when used get: you get the actual value but when an update occurs for my-tabs it won't update my-button leaving old data in display.
  • when used set: the new value will be considered in my-tabs and rendered accordingly. nothing affected.

now I think about array of components being stored in a global array with onconnect/disconnected callbacks)

I know it's a big no-no for reusability but I build my project using lit and never plan to publish the components. most of them are already very specific for the project. so yeah, I know it's bad for reusability but what if I don't care about it. can it give some other side effects? or is it simply ok to? what about the array one? thanks in advance <3

2 Answers2

1

I would consider using events instead.

If MyButton is a child of MyTabs, you can have MyButton dispatch it from itself with bubble and compose, and have MyTabs listen for it.

If they are siblings, then you may need to do it on some common parent, or window.

class MyButton extends LitElement {
    dispatchTabChange(tabIndex) {
      const event = new CustomEvent('tab-change', {detail: tabIndex});
      window.dispatch(event);
    }

    render() {
        return html`
            <button @click=${() => dispatchTabChange(2)}>back to tab 2</button>
        `
    }
}

class MyTabs extends LitElement {
    static properties = {
        active: {}
    }
    constructor() {
        super()
        this.active = 0
    }
    tabChangeListener(e) {
        this.active = e.detail;
    }
    connectedCallback() {
        window.addEventListener('tab-change', this.tabChangeListener);
    }
    disconnectedCallback() {
        window.removeEventListener('tab-change', this.tabChangeListener);
    }
}
Augustine Kim
  • 841
  • 1
  • 5
  • oh this makes so much more sense than my "no get only set" rule since it's already not possible and seeing `tab-change` as a global event rather than a "setting property of a promised to be single component" is more comforting and semantically better. thanks I think this was the answer I wanted to get. – Vagif VALIYEV Jul 25 '23 at 02:00
0

It is your code, you can add as many globals, pigeons or telegraph poles to your code as you want.

Your HTML probably looks something like:

<my-tabs>
 shadowroot
  <my-tab id="tab1"></my-tab>
    shadowroot
      <button></button>
  <my-tab id="tab2"></my-tab>
    shadowroot
      <button></button>      
  <my-tab id="tab3"></my-tab>
    shadowroot
      <button></button>
</my-tabs>

"problem" with Lit is, by default it sticks a shadowDOM on every element.

That means you can't use the default .closest("my-tabs") to walk up the DOM

You can add a closestElement("my-tabs") method that crosses shadowroot boundaries issued from your <button>

  closestElement(selector, el = this) {
    return (
      (el && el != document && el != window && el.closest(selector)) ||
      this.closestElement(selector, el.getRootNode().host)
    );
  }

It might be easier to only keep ONE shadowDOM on <my-tabs> and non on <my-tab>; makes closest() work and styling a whole lot easier

Danny '365CSI' Engelman
  • 16,526
  • 2
  • 32
  • 49
  • appreciate the effort. my tabs element is kinda similar to your snippet but there's one more button which is not a child of tabs. also this makes a query. so isn't setting a global reference at constructor better for that? – Vagif VALIYEV Jul 22 '23 at 17:53
  • Its your call, there is no "better" in programming. – Danny '365CSI' Engelman Jul 23 '23 at 06:48
  • there is. but I wasn't clear, I meant "optimal" - if I can reach that reference without a query, then it's an unnecessary step that hits (even if it's small) performance – Vagif VALIYEV Jul 24 '23 at 13:12
  • You should also replace all ``let`` and ``const`` with ``var`` then... its faster. And replace all functional programming with ``for`` loops. – Danny '365CSI' Engelman Jul 24 '23 at 14:35
  • welp, there's a lot of things to consider if we're talking about `var`, `let` or other stuff that are the base of the programming language which can be used for anything that we can't even think of. but for a this specific case, the speed is the only parameter that I could think of **and** I asked if there's other things to consider. that's the whole point of my question. I'm sorry if I annoyed you with this little performance complain. just wanted to look from different angles. thanks again – Vagif VALIYEV Jul 24 '23 at 15:29