Medidate: A Meditation App built with Rails and AngularJS

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 :events
has_many :meditations, through: :events

class MeditationSerializer < ActiveModel::Serializer
attributes :id, :name
end

end

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:

Screen Shot 2017-03-13 at 9.09.13 PM

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

resources :users

end

end

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:

{
“directory”: “vendor/assets/bower_components”
}

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.

Authentication

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.

Toaster

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:

Screen Shot 2017-03-14 at 3.01.08 PM

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:

Screen Shot 2017-03-14 at 3.08.30 PM

Now the icon for the success pop up is my very own and the app looks much more congruent.

Screen Shot 2017-03-14 at 3.35.51 PM

Before

Screen Shot 2017-03-14 at 3.39.45 PM

After

 

Conclusion

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.

AngularJS: Using $localStorage to Keep Users Logged In

If you are familiar with authentication, then you are should be familiar with sessions and web tokens. While the latter is the standard these days, sessions still have their place, especially when building small apps. The app I’m currently working on is using a Rails API with an Angular front-end. Since it was my first time attempting to use authentication with Angular, I used the tried and true Devise gem for Rails and Angular-Devise for Angular. Devise was fairly simple to set up and I had it working pretty quickly. Their documentation is pretty good as far as documentation goes!

I was saving my user info and their signedIn status in the $rootScope of my app which would make it accessible to all of my views and controllers. This worked well until a page was refreshed and my $rootScope was demolished along with all my users signedIn status. This is a good thing to know about Angular! The rootScope starts fresh on every page refresh. After reading up and doing some soul searching, I found a solution.

ngStorage to the Rescue

The ngStorage module allows you to store information that you want to have available during a session. After bowering or npm-ing, all you have to do is add ngStorage to your apps dependencies and inject $localStorage or $sessionStorage into any controller you need it in. Pretty simple. Now to use it, you have to pass $localStorage by reference to a hook under $scope or $rootScope, like so: $rootScope.$storage = $localStorage. Add this line to your controller and you have access to all the goodness that you would like to store there.

The basic usage would be to pass a boolean to your currentUserSignedIn variable on the $storage when they are logged in. Using Devises’ Auth.login function, you can then add the users status in the callback function.

Screen Shot 2017-03-06 at 10.01.13 AM

 

Ignore the toaster.pop (although check it out, it’s cool!) stuff going on and pay attention to the $rootScope.$storage line. Here we set currentUserSignedIn to true and have that information available in our views. Since it’s set on the $rootScope, we should have access to it throughout our app. Now on page reloads, we still have access to our users’ signed in status. Cooool!

I have a navbar throughout my views which uses its own controller. Since I used $rootScope.$storage I should have access to the users’ status. There was one occasion, however, where I would lose access to the $storage variable on a page reload. I wasn’t quite sure what was happening, but when I added $scope.$storage = $localStorage to my navbar controller, everything worked fine again. Don’t forget to inject $localStorage into the navbar controller. Now in the navbar html template, if we wanted to have a link to show “Log In” if the user is not logged in we could use a ng-hide directive as such:

screen-shot-2017-03-05-at-6-57-27-pm

 

If our user is logged in, we can simply change the hide to a show for a Log Out link:

screen-shot-2017-03-05-at-6-58-30-pm

When our user signs out we can reset our $localStorage or set the currentUserSignedIn boolean to false. We do this just like we did in the sign in function:

Screen Shot 2017-03-06 at 10.15.36 AM

Here we set currentUserSignedIn to false and our users session is complete. I’m definitely going to check out JWT Tokens next, but baby steps are good to take with these things. Thanks for reading!