Programming with Meteor and Materialize

Share on FacebookShare on Google+Tweet about this on TwitterShare on LinkedIn

firstpage

Our goal at the Crisp hack summit last weekend was to migrate our 2 year old shopping app written in Meteor to the latest version and to learn about Google’s material design. Our old app was built as a way for us to learn Meteor. The structure is less than ideal, and as we learn new things we add them to the app, but don’t revisit old parts. So we followed Dan North’s experiment rewriting the app from scratch. We also decided to use Materialize for the UI. We wanted to rewrite the app in 2 days, keeping all the functionality we currently have, but at the same time adding the UI and usability improvements that we really need.

We ended up completing the rewrite in 9 days: 2 hack days and then a couple of hours each day for the next week. Not too bad for a brand new app, but surprisingly longer than we would have guessed. Both Meteor and Materialize are pretty simple to get started with, but adding Materialize to Meteor proved to be challenging. Here are some highlights!

Installing Meteor

One of our development machines already had Meteor installed, so all we needed to do was to run meteor update and wait for the magic to happen. The other machine did not. Installing Meteor however, is pretty simple. You just need to run a shell script file from Meteor’s site:

Screen Shot 2015-10-24 at 10.07.18

Huh, first unexpected issue. We ended up having to download the sh file, modify the Meteor version to 1.2 then run the install script, and finally run a meteor update. Minor hiccup, but googling around showed us that others had been experiencing the problem for the past couple of weeks. It really should have been fixed by the time we got around to installing.

Installing, then reinstalling Materialize for Meteor

There’s a Meteor plugin for Materialize, installation was a no-brainer:

meteor add materialize:materialize

We didn’t need to make any changes, the css and JavaScript libraries were available right away, easy peasy.

Within two days we had to uninstall. Materialize has a Sass implementation that’s not available for Meteor, but if you’re not using the Sass version you can’t easily make changes to your color theme. You have to manually change the colors on each individual element, our HTML code was quickly becoming unreadable with all the color classes added everywhere, and it was increasingly diffScreen Shot 2015-10-24 at 10.19.13icult to keep track of all the elements that we had to reset colors on. So we switched to using an independent Materialize Sass package. Installation is simple, but a bit more work than using the plain vanilla Materialize since you need to add your own scss file and make sure you include the Materialize libraries in the right order to get the setup correct. The instructions are clear and easy to follow though, so it’s not a problem.

Pretty painless for the freedom it gave us. Everything worked as intended, and we could clean up our markup. We then used material palette to help us pick a color theme.
Screen Shot 2015-10-24 at 19.53.37

Meteor’s onRendered function

Quite a few Materialize elements need to be initialized with JavaScript hooks, listeners or functions. On dynamically created elements these need to be run after the element is rendered in the browser. Since we have a one page app that redraws for each “page” we have, pretty much all of our elements are dynamically rendered. Luckily, you can use the onRendered function for a template, and as soon as the template elements are rendered it gets called and you’re golden.

We quickly ran into an interesting limitation. Materialize has a pretty convoluted solution for select-option which requires you to add a hook to the select, once all the option elements are rendered. We were populating the options using data from a helper, and we learned that the onRendered function runs as soon as the html elements in the template are rendered, but NOT necessarily after all the helpers are run. So if you’re on a slower network and your helper takes a bit longer to execute, the html will be rendered and the onRendered call will be run, before your markup is complete. To work around this we had to subscribe to the object that the helper was dependent on and then attach the Materialize hook. This sounds like a really niggly point, but the problem here isn’t Meteor’s implementation, rather the fact that this is not mentioned as part of the documentation. We spent quite a bit of time trying to understand what was going on. The problem wasn’t apparent when running on a local host, or when using wifi, it only showed up when we were trying to demo on 4g. This situation underlines the need for accurate api documentation, in fact the doc specifically mentions that “This can be a good place to apply any DOM manipulations you want, after the template is rendered for the first time.”

Screen Shot 2015-10-24 at 10.27.45

We eventually decided to use a different Materialize element that could be initialized before any helpers were run, to make the code a bit cleaner. We’re not big fans of having to decide which UI elements to use based on ease of implementation or code readability. But in this case the Materialize implementation of a select-option element combined with Meteor’s limitations led us to switch to a different implementation. Ideally Materialize would simplify their implementation so that you don’t need to add a hook after all the options are rendered (maybe by solving the UI without having shadow dom elements). While Meteor would either make the onRendered do what you think it does after reading the documentation, or update the documentation and give a suggestion for how to handle helpers: should we use defer? subscribe? Something else altogether?

Materialize’s collapsibles

The collapsible JavaScript was very easy to use, and worked like a charm. Then we decided to add secondary elements to the main element. Maybe our UI is too complicated/unusable, but we really didn’t want two views for editing information for a store. We had a list of stores, we wanted to be able to edit the name, or delete the store, as well as provide the ability to sort the items you can buy. We figured a collapsible, that also has secondary elements that can be clicked for secondary functions was the best solution.

Screen Shot 2015-10-24 at 10.53.05

We expected to be able to click on the edit or delete icons and attach event listeners to the click event. It turns out we never got to the click event. Materialize’s implementation of collapsible event listener stops propagation for all other events. Luckily there are others facing the same issues and we were able to implement an inspired hack from https://github.com/Dogfalo/materialize/issues/1996. This time since we really did like the UI we decided to live with the uglier code.

Screen Shot 2015-10-24 at 10.55.54

What we learned

We feel that the rewrite approach was the right way to go. It allowed us to clean up all the stuff that we weren’t happy with, but we felt that it would have been more effective if we had recently been working with the code. Neither of us works with Meteor on a daily basis, we’d forgotten a bunch of stuff that we had learned the first time, and we were learning new stuff at the same time. We would have done a better rewrite had we been more familiar with our own old code, but rewriting from scratch was a better option than cleaning up the old implementation.

One challenge with the rewrite, that we didn’t think we’d face, was figuring out where to start. We had a much easier time defining our MVP last time when we started with nothing. This time we had code and solutions and a more complete product vision and goal. We needed to decide which feature to start with, and we could have really started anywhere. In the original implementation we added the database as a last step, using hard coded data for the first iterations. This time we had it as part of our MVP, since we wanted to make sure that the whole flow would work before going forward. Once we got going it was easy to break down the remaining work and finish it in small chunks, and we don’t regret having the db as part of the initial MVP.

Using Materialize gave us a really nice polished site, but it came at the expense of a lot of extra work. Last time we wrote all our own css by hand, and while our UI didn’t look so nice, we also didn’t have a bunch of JavaScript hooks that had to be timed to work right. We spent most of our time trying to get Materialize to work with Meteor. We’d definitely like to see Materialize mature before we’d use it for a real life project, but it shows real promise.

ES6 => It’s nice to be able to use “let” and “const” instead of “var” everywhere, correctly scoped variables make working with code much more enjoyable! The arrow function also makes the code cleaner.

Lastly migrating our database from the old Meteor instance to the new one was a snap! We kept the same data structures we had last time (we’re not convinced that they’re the best, but they are good enough) so we could run Mongo’s mongodump and mongorestore and have our data to use in the new app. The migration worked like a charm, and it turns out that the useraccounts package hasn’t significantly changed their implementation so we could even migrate all our user accounts. An added bonus is that useraccounts supports Materialize, so we have a much cleaner UI even for the log in this time.

We love that we can host the app on meteor.com, and it’s painless to create an account and get setup. We also use github which makes working on two computers simultaneously painless! We each use our own laptop when we’re at keyboard, and then push our changes when we want to switch driver. When we pair program we take turns implementing whole tasks at the keyboard. If the person at keyboard doesn’t know what to do next the other person looks up solutions and helps out, but the driver is the one who codes the whole task. At other times we work independently and keep the other person in the loop.

Screen Shot 2015-10-24 at 11.30.19

You can find our project on GitHub and deployed on Meteor. It’s a work in progress and a learning tool for us, but it’s also an app that’s in use and adds value to us. In the settings you can add items that you usually shop for, and then add stores, where you can order the items in shopping order if you’d like. Finally you can add a subset of items to the shopping list in edit mode, then go shopping in the shop mode 🙂

 

4 Comments

  • 1
    Phil
    2016-02-15 - 14:58 | Permalink

    Hi guys,

    i just added fourseven:scss and poetic:materialize-scss the way you did + i removed materialize:materialize. but this move breaks my whole app… the meteor server starts but returns blank white pages … the console return 22 errors starting with: Uncaught Error: Failed to require “jQuery” from “root” ..probably this error causes the 21 following errors. i contacted POETRIC via facebook but they do not answer me.

    Have you guys any clue? 🙂

    • 2
      2016-02-15 - 15:17 | Permalink

      Hmm, no not off the top of my head. That was the one area where we didn’t have any problems. Let us know what the solution is when you find it!

  • 3
    Sid Kwakkel
    2016-08-08 - 21:16 | Permalink

    Which materialize component did you use in replacement of the select?

  • Leave a Reply

    Your email address will not be published. Required fields are marked *