aurelia-ui-toolkits / aurelia-materialize-bridge

Materialize CSS components for Aurelia
http://aurelia-ui-toolkits.github.io/demo-materialize/
MIT License
156 stars 53 forks source link

Creating tabs from a collection of object results in "Cannot read property 'classList' of undefined" in materialize.js line 4204 #547

Closed gerfen closed 5 years ago

gerfen commented 5 years ago

I'm creating tabs from a collection which is being retrieved from an async REST call. My markup looks like this:

<ul md-tabs md-on-selected.delegate="onTabSelected($event)">
    <li class="tab" repeat.for="location of locations" md-waves="color: primary;"><a class="${ $first ? 'active' : '' }" href="#${location.id}">${location.name}</a></li>
</ul>
<div repeat.for="location of locations" id="${location.id}" class="tab-content z-depth-1">
... elided
</div>

The tabs fail to render with the following error:

materialize.js:4204 Uncaught TypeError: Cannot read property 'classList' of undefined
    at Tabs._setupActiveTabLink (materialize.js:4204)
    at new Tabs (materialize.js:4011)
    at MdTabs.push.aurelia-materialize-bridge/tabs/tabs.MdTabs.attached (tabs.js:60)
    at Controller.attached (aurelia-templating.js:3757)
    at View.attached (aurelia-templating.js:1770)
    at ViewSlot.add (aurelia-templating.js:1933)
    at If._show (if-core.js:59)
    at If._update (if.js:95)
    at If.conditionChanged (if.js:81)
    at BehaviorPropertyObserver.selfSubscriber (index.js:99)

The code in materialize.js looks like this:

{
      key: "_setupActiveTabLink",
      value: function _setupActiveTabLink() {
        // If the location.hash matches one of the links, use that as the active tab.
        this.$activeTabLink = $(this.$tabLinks.filter('[href="' + location.hash + '"]'));

        // If no match is found, use the first link or any with class 'active' as the initial active tab.
        if (this.$activeTabLink.length === 0) {
          this.$activeTabLink = this.$el.children('li.tab').children('a.active').first();
        }
        if (this.$activeTabLink.length === 0) {
          this.$activeTabLink = this.$el.children('li.tab').children('a').first();
        }

        this.$tabLinks.removeClass('active');
        this.$activeTabLink[0].classList.add('active');  <--- FAILS HERE

        this.index = Math.max(this.$tabLinks.index(this.$activeTabLink), 0);

        if (this.$activeTabLink.length) {
          this.$content = $(M.escapeHash(this.$activeTabLink[0].hash));
          this.$content.addClass('active');
        }
      }

If I click back and forth on a navbar in my application the tabs will eventually render correctly. Any ideas on what I might try to get the tabs to render when my locations collection is returned from my REST call?

gerfen commented 5 years ago

I was able to hack around this issue by making a change to the underlying materialize-css tabs.js code -- basically checking if there are any tabs before trying to set the active css class to the first <li/> tag, adding a reference to aurelia-async-binding along with adding & async where appropriate to my bindings in my view and calling tabs.refresh() after my data is returned from my REST call in my view model. It's an ugly hack IMO but it's got me moving forward. I would still like to get some advice on a better approach.

carusology commented 5 years ago

@gerfen

Does adding an element that wraps your <ul> tabs element with if.bind="locations.length" work?

<div if.bind="locations.length">
  <ul md-tabs ...>
  </ul>
</div>