material-components / material-components-web

Modular and customizable Material Design UI components for the web
https://material.io/develop/web
MIT License
17.15k stars 2.15k forks source link

Can't switch between dynamically created tabs in MDCTabBar. #4214

Open vedtam opened 5 years ago

vedtam commented 5 years ago

Hi guys,

In my element I'm displaying category titles as Tabs. The problem is that I can't switch between tabs which are rendered after the categories get fetched:

...
    <div class="mdc-tab-bar" role="tablist">
        <div class="mdc-tab-scroller">
          <div class="mdc-tab-scroller__scroll-area">
            <div class="mdc-tab-scroller__scroll-content">

              <button class="mdc-tab mdc-tab--active" 
                      role="tab" tabindex="0" aria-selected="true">
                <span class="mdc-tab__content">
                  <!-- <span class="mdc-tab__icon material-icons">favorite</span> -->
                  <span class="mdc-tab__text-label">VIEW ALL</span>
                </span>
                <span class="mdc-tab-indicator mdc-tab-indicator--active">
                  <span class="mdc-tab-indicator__content mdc-tab-indicator__content--underline"></span>
                </span>
                <span class="mdc-tab__ripple"></span>
              </button>

              ${Object.values(categories || []).map((category) => html`
                <button class="mdc-tab" 
                        role="tab" tabindex="0" aria-selected="false">
                  <span class="mdc-tab__content">
                    <!-- <span class="mdc-tab__icon material-icons">favorite</span> -->
                    <span class="mdc-tab__text-label">${category}</span>
                  </span>
                  <span class="mdc-tab-indicator">
                    <span class="mdc-tab-indicator__content mdc-tab-indicator__content--underline"></span>
                  </span>
                  <span class="mdc-tab__ripple"></span>
                </button>
              `)}

            </div>
          </div>
        </div>
      </div>
  `;
  }

...

stateChanged(state: RootState) {
  this.categories = state.category.items;
}

firstUpdated(){
   new MDCTabBar(this.shadowRoot.querySelector('.mdc-tab-bar'));
}

I've created a demo on glitch that demonstrates the issue, you can see the source here.

I've tried to look for answers on discord, but I got no proper answer there. Thanks!

kfranqueiro commented 5 years ago

Reproduction with vanilla MDC Web: https://glitch.com/edit/#!/mdc-web-4214

We're only ever instantiating tabs based on the DOM during initialize. We would probably need to add an explicit addTab (and removeTab?) API that would receive an mdc-tab element (or possibly an index for removal), or something like reinstantiateTabs that would destroy all current tab instances and reinstantiate based on the current state of the DOM.

vedtam commented 5 years ago

@kfranqueiro thanks for the clarification. Well as a workaround, I moved the 'static' tab inside the loop that generates the rest of the tabs after data gets fetched, now all tabs render simultaneously but I can see other cases where this wouldn't be an option so having an add/remove Tab or reinstantiateTabs method would be nice.

Rajeshbhartia commented 4 years ago

I think you might forget to import those files

import {MDCTabBar} from '@material/tab-bar'; const tabBar = new MDCTabBar(document.querySelector('.mdc-tab-bar'));

KrisSodroski commented 2 years ago

Is there any update on this.

It seems a lot of the mdc controls require that the data be statically passed to any implementation. It makes the controls rather unsuitable to use.

To work around this, I had to catch my tab-item render event, and hack the MDC tab into the tabList of the tabAdapter.

On top of this, there seems to be a global tracking of these tab items, so each tab-item must have a unique id globally.

  const anyTabBar = this.TabBar as any;

  anyTabBar.tabList.push(Tab);

  const randomId = Math.round(Math.random() * Number.MAX_VALUE);

  const id = `mdc-tab-${ randomId }`;

  Tab.id = id;

  Tab.root.id = id;
JoshMayberry commented 2 years ago

I am also interested in some kind of add/remove Tab or reinstantiateTabs solution.