After reading Nick Wientge’s great post on using the Materialize framework to add Material Design ideals to your Meteor project, I was eager to jump on board.

The CSS and static component side of the Materialize framework is fantastic! After a few hours, I had converted a Bootstrap project over to a more Material Design inspired aesthetic, complete with schnazzy animations and transitions.

Unfortunately, I began to hit a few roadblocks when I started combining Materialize’s javascript components with reactive data from Meteor.

Form Select

The application I was converting to Materialize made heavy use of reactively populated select elements. I figured the transition to Materialize would be as simple as calling material_select when the select was rendered:

Template.select.rendered = function() {
    this.$('select').material_select();
};

But, since Materialize mirrors the options data in a hidden list in the DOM, we’ll need to re-initialize the select plugin every time the data changes. No problem:

Template.select.rendered = function() {
    this.autorun(function() {
        this.data.options; //autorun trigger goes here
        this.$('select').material_select();
    });
};

And, it doesn’t work! At least with the most current Materialize release at the time of this post (v0.95.3). In v0.95.3, re-running the material_select plugin will not re-generate the options list, even if new options have been added to the select. Thankfully, this has been reported as a bug and subsequently fixed, but you’ll need to grab to latest code for yourself to make use of the fix.

These issues can also be entirely avoided by adding the browser-default class to your select. This will cause Materialize to not mirror your select’s options in the DOM and use a styled version of the native select instead. Reactivity will work out of the box, as it would for any other select element.

Collapsible Elements

Collapsible elements also have issues with dynamic content. Collapsible elements are initialized by calling the collapsible plugin on the collapsible list. This will turn all of the child list items into collapsible containers:

<template name="collapsible">
    <ul class="collapsible">
        {{#each items}}
            <li>
                <div class="collapsible-header">{{header}}</div>
                <div class="collapsible-body">{{body}}</div>
            </li>
        {{/each}}
    </ul>
</template>
Template.collapsible.rendered = function() {
    this.$('.collapsible').collapsible();
};

But what happens when another item is added to items? We’ll need to re-initialize the collapsible plugin:

Template.select.rendered = function() {
    this.autorun(function() {
        this.data.items; //autorun trigger goes here
        this.$('.collapsible').collapsible();
    });
};

Unfortunately, this doesn’t work exactly as we expected. While the new item is collapsible, re-initializing the plugin also closes all currently open items. If we dig into the source, we can see why this happens.

Let’s take a look at the “expandable” data path. When the plugin is initialized, it loops over each collapsible-header and checks its active status. If it is active, it calls expandableOpen. Take a look.

expandableOpen toggles the active class on the collapsible-header’s parent (li), and then checks its value. If it is active, it expands the container, otherwise it collapses it. Check it out. The re-initialize issue happens because the parent li already has the active class for previously initialized items. When we toggle the class, we inadvertently close the container.

The accordion data path is a little more complicated, but the same general issue exists.

I’ve created an issue and a pull request to fix this issue with the collapsible plugin. Go open source!

Final Thoughts

Materialize is a great front-end framework. It allowed me to quickly and easily build a Material Design style application.

That being said, I don’t think it’s the best fit for a Meteor application. I’m not interested in dealing with the unnecessary complexity of managing the initialization and re-initialization of each of my components as they’re reactively added to the DOM.

At the end of the day, I believe I’m better off using Polymer as a static component library to build my Material Design applications.