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!
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:
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
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 difficult 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.
Meteor’s onRendered function
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.”
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?
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.
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.
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
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.
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 🙂