I’m just finishing up my new app. It’s a meditation app called ‘Medidate’, that keeps track of your meditations – the type of meditation, how long you meditated, and the date. You can also explore new meditation techniques. I built it using Rails on the back end as an API and AngularJS on the front end. Since it was my first time using the two frameworks together, it was quite a challenge. I’m going to walk through some of the steps to get these two frameworks to play nice together and also show some cool modules that I used.
Rails as an API
First off, to get Rails set up as an API is easy-peasy. The necessary gem is ‘active_model_serializers‘ and after bundling we only have to do a few things to get up and running.The app has three models – user, meditation, and event. After setting up the models, to generate the serializers we run ‘rails generate serializer <model name>’ in the terminal. Then we’ll set up our associations in each serializer. The User Serializer looks like this:
class UserSerializer < ActiveModel::Serializer
attributes :id, :username
has_many :meditations, through: :events
class MeditationSerializer < ActiveModel::Serializer
attributes :id, :name
Notice how the MeditationSerializer is nested inside the User. This allows us to dictate exactly what information we want from the Meditation model. In this case just the id and name will suffice. This is not a big app so this doesn’t matter that much. But in an app that is large and has a lot of information being retrieved from the database. Instead of getting every attribute of Meditation, we’re now only getting two. Much less information getting pulled from the backend. The Event and Meditation Serializers look pretty similar.
Next, on to the controllers. Here, we’ll have our CRUD actions render json from the serializers. Here’s the Events Controller:
Pretty standard looking for a Rails Controller. You might’ve noticed that it’s namespaced as an API with version control. This will allow us to create new versions in the future without breaking something. Now on to Routes.
When creating the routes we have to remember about the name spaced controllers. Instead of the usual ‘resources :users’, now we have to nest the resource like so:
namespace :api do
namespace :v1 do
We’ll do the same for events and meditations and add one more route for our root which will be:
root to: ‘application#angular_home’
And we’re good to go.
Setting Up Angular
Getting Rails and AngularJS to play nice was difficult at first. I went with Bower to install my dependencies for Angular. Installing wasn’t a problem, but when I tried to require the dependencies in the main ‘application.js’ file, the dependencies were not found. What to do? I realized that when I installed the dependencies with bower, they were not being saved in the right place. In order for Rails to find the files, they have to be in the asset pipeline. That means they have to be in an ‘assets’ folder which are found in these places:
‘app/assets’, ‘lib/assets’, and ‘vendor/assets’
Angular dependencies should be kept in the ‘vendor/assets’ folder, so I moved the bower components folder there. Everything was good after I did this, however when I installed new dependencies with bower, they would wind up saving in a different place. After a little digging I found the culprit – the .bowerrc file. It basically tells bower where to save components when installing. Here’s what I had to do in .bowerrc:
Now when installing dependencies from the command line, everything was saving to the correct folder! One more thing – add ‘vendor/assets’ to the gitignore file so all the dependencies are not pushed to github.
There are many ways to authenticate these days. Since I was familiar with Devise, having used it a bunch in Rails, I decided to stick with it. Using the ‘devise‘ gem for Rails and ‘angular-devise‘ for Angular, it wasn’t too tough to get things moving. The hard part was keeping the user signed in after they were authenticated – persistence. I tried putting a currentUserSignedIn variable on the $rootScope, but on a page refresh, the rootScope gets destroyed, so that was a no go. I ended up using ngStorage, a cool little module that gave me what I needed. Basically, it gives you a storage variable that will be persisted throughout the app. Check out my other blog post here for more information on usage.
I wanted to add some user interactivity to the app, you know, like pop ups when you submit a form. I ended up using AngularJS-Toaster. It’s pretty simple to use. After installing it with bower and requiring it in ‘application.js’, you only have to inject it in the main app module and the controller of your liking. Then you just pop it in to a function, like so:
As you can see, the Toaster module gives us a few options. I only had the need for two of them: ‘success’ and ‘error’. ‘toaster.pop’ takes three arguments: the type of message (ie success), the title, and the message. I just used the title as you can see above. I was able to tweak the message colors and icons so they were more inline with my app. In my scss file I basically overrode the default icon like so:
Now the icon for the success pop up is my very own and the app looks much more congruent.
This app is not deployed yet, but if you want to check it out, go fork it from github. I learned a ton by building this app. Angular and I were definitely not friends before I started this app, yet slowly but surely we grew on each other.