In this post we are going to take a look at how to use Bootstrap Modals in conjunction with Meteor. The main things we’ll cover are:
- How to set-up a bootstrap modal to work with Meteor.
- How to use a 3rd party package to make working with bootstrap modals easier.
- And finally, although not related to modals, we’ll figure out how to create lists which can be sorted by our user’s.
As a starting point, we’re going to use a very simple existing Meteor app, if you want to start from scratch, see part 1.
If you’d rather skip directly to the code instead of following along, it’s available on GitHub.
What we’ll build
To demonstrate working with modals we’ll build out a very simple application that let’s user’s rank their favorite animals. The main interface will look like:
User’s can add more animals via the “add” button, which will bring up a modal:
Creating the app
As a starting point, we’ll clone a partially implemented version of the application from GitHub. This partial implementation has the list of animals implemented and the ability to delete animals. We’ll need to implement the add, edit and ranking functionality.
Clone the Repo
Note, if you aren’t familiar with Git and / or don’t have it installed you can download a zip of the code here. Otherwise let’s git started (you can steal that joke if you want, it works great on dates… OK, maybe not so much).
Terminal
A quick over-view of where we’re starting from
If you open the code in your text editor of choice, you’ll see a pretty standard file structure.
The animals.html
template is the core template for the application, with animal.html
being responsible for the rendering of single animal records.
We’ll primarily be changing / adding code in the /client/templates/animals
folder, in addition, we’ll make some changes to /lib/collections/animals.js
.
Start up the app
Terminal
You should now see the application render when you pointing your browser to http://localhost:3000.
Adding animals
Hooking up the Add button
The first thing we’ll work on is to implement the “Add” button functionality. If you click the “Delete” links you’ll notice it’s possible to remove animals from the list of favorites, but there is currently no way to add new animals, that won’t do!
First step is to create our modal template and include it as part of the existing animals.html
template. We embed the modal template within the animals template as the modal needs to be rendered as part of the animals template in order for it to open successfully. Including the modal as part of the animals template is not ideal, we’d rather put it in it’s own template file. Later in this post we’ll make use of a third party Meteor library that will allow us to do just that… but for now:
/client/templates/animals/animals.html
OK, nothing special about the animalsModalTemplate
, it’s a standard Bootstrap modal. The key thing to note is the {{> animalsModalTemplate}}
tag we’ve added to the animals
template. As mentioned earlier, unless we render the modal as part of the main template it won’t appear when we try to open it.
So with the HTML in place, let’s add an event handler to animals.js
.
/client/templates/animals/animals.js
After the above changes, the modal will appear when the “Add” button is clicked.
Saving new animals
So we now have our Modal showing up, and although the “Cancel” button works out of the gate, the “Save” button is currently nothing but an empty promise. Let’s fix that!
First we’ll capture the Save button event.
/client/templates/animals/animals.js
Pretty simple, we’re grabbing the value entered in the name text box and then calling a Meteor method on the server. Of course, we have yet to implement the server method, so we’ll currently see an error if we try adding an animal.
OK, so let’s get rid of that no method error by… you guessed it, implementing the method.
/lib/collections/animals.js
This is super simple, we just need to check that the name field is a valid string, then we add the animal to our database. Just prior to inserting the record we add a default rank value for the animal. Later on, we’ll use the rank value to allow users to rank the animals.
Time to refactor!
Including the Modal code in animals.html
and animals.js
is rather messy… and things are only going to get worse as we add more functionality. Let’s see if we can structure our code a little better.
Switching up our Modal to use a package
Luckily there is a Meteor package we can use to split out our Modal from our other templates.
Terminal
Now we’ll move our modal template code out of animals.html
into it’s own file.
Terminal
/client/templates/animals/animals-modal.html
One thing to notice is that we’ve renamed the template to “animalModal” and we’ve removed the id attribute on the top-level div. Another advantage of the package we’re using is that it triggers the modal based on the template name so a separate id is not required.
Now, let’s change animals.html
.
/client/templates/animals/animals.html
The file is now much cleaner, also notice we no longer need to render our modal template in the animals template, i.e. the {{> animalModalTemplate}}
line has been removed.
OK with those changes, out of the way, if you click the Add button you’ll notice nothing happens. This is because we need to call our modal in a different manner, so let’s get that sorted. We’ll also move the modal specific code out of animals.js
into it’s own file just like we did with the template code.
Terminal
/client/templates/animals/animals-modal.js
OK, we’ve moved the save animal event handler into it’s own file, and we’re using our package to hide the modal, i.e. Modal.hide('animalsModal');
.
Now we’ll clean up animals.js
.
/client/templates/animals/animals.js
We’ve removed the save animals event handling code and changed up our code that opens the Modal to use the package, Modal.show('animalsModal');
.
And with that, we’ve finished our refactoring and the Add button is back to a working state. Next step is to implement edit.
Editing animals
As you can see below, I’ve got a typo with my newly added animal.
I could delete and re-add my Donkey… but that seems a little awkward, so let’s get some editing up and running.
Altering the UI to support editing
First off let’s add an edit link to animal.html
.
/client/templates/animals/animal.html
Now we just need to hook up the event so that our modal is opened when the user clicks “Edit”.
/client/templates/animals/animal.js
Easy as pie… not! The above code opens up the modal but the existing animal name isn’t showing up.
Essentially we need our modal to handle two modes, “Add”… when a user adds a new animal, and “Edit”… when a user is editing an existing animal.
When the modal dialog opens in edit mode we need to populate the modal with the details of the current animal under edit. We’ll accomplish this by setting a Session variable that will contain our animal id. If the id is empty we’ll know the modal has been opened in create mode.
Note: Session variables are handy but should be used with caution as they have the same drawbacks associated with traditional global variables.
In any case let’s make our changes.
/client/templates/animals/animal.js
/client/templates/animals/animal.html
There’s a decent amount going on here but it’s fairly straight-forward. In animal.html
we’re using a data-id attribute to keep track of the id of the current animal record. We then use this in conjunction with the class attribute to grab the id in animal.js
.
What’s up with that ModalHelper.openModalFor(animalId)
line? Well since there is now a little bit of logic involved in opening our modal, we’re going to create a helper to centralize that functionality.
Terminal
/client/helpers/open-modal.js
The helper code just sets a session variable prior to opening the modal.
Next let’s make use of the session variable in the modal.
/client/templates/animals/animals-modal.js
Firstly we’ve updated the animal helper to fetch the current animal record when in “Edit” mode, i.e. when the Session variable is populated. Next we update the save method to call different methods on the server depending on whether an add or an update is occurring.
Finally we need to update animals-modal.html
to set the value of the animal name text box. This way it will be blank if it’s a new instance, but populated when an edit is under way:
/client/templates/animals/animals-modal.html
Oops, I lied, one more small edit is required in animals.js
.
/client/templates/animals/animals.js
In the ‘add’ event handling code we need to call the ModalHelper method we created and pass null
as the animal id. If we fail to do this the Modal will contain the name of the last edited animal when it opens, as our selectedAnimalId
Session variable will still be set.
So that is the client side code, but we’re calling into a non-existent server method, ‘editAnimal’, so let’s code that up.
Altering the back-end to support editing
/lib/collections/animals.js
Pretty darn easy!
We’ve now got our modal working the way we want both when adding new animals and when editing existing animals.
Ranking animals
The final step is to allow user’s to rank the animals via drag and drop. For instance Dogs and Donkeys definitely need to come before Cats:
We’ll accomplish the above with the help of the drag and drop functionality included in jQuery UI.
Terminal
A little bit of Googling yields a great article that covers exactly what we want to do: http://blog.differential.com/sortable-lists-in-meteor-using-jquery-ui/.
Excellent! We can copy the code from the above blog post pretty much verbatim to get our drag and drop hooked up. We’ll add the code from the Differential blog post to animals.js
, hooking into the rendered event of the template.
/client/templates/animals/animals.js
Now all we need to do is implement the updateAnimalRank
method on the server.
/lib/collections/animals.js
And that’s it! User’s can now edit, add and rank animals.
Summary
Although a contrived example (heck we don’t even have seperate lists of animals for different user’s), we see that using Modals within Meteor is pretty simple. Likewise adding nice UI elements such as drag and drop can also be accomplished without too much effort.
Thanks for reading and hope you found this post useful!