AngularJS: Filter – Behind the Scenes

AngularJS comes with a ton of built in features to help us out – meaning we don’t have to reinvent the wheel every time we want to do something. This makes it extremely easy to do things such as searching or filtering. However, sometimes it’s a good idea to check what’s going on under the hood, which can only help us in the end. These helpers are great, but if you’re knew to programming, they could stunt your growth as a developer. So let’s dive in.

I built an app called Medidate. It’s a simple (a lot of hours goes into simple!) single page app that keeps track of meditation sessions: the date, which kind of meditation, and the duration. It uses Rails as an API on the back-end and AngularJS on the front-end. I’m going to go over a sorting feature that could easily be implemented with Angular’s built in Filter helper. Here’s a Plunker with the easy way.

The Easy Way

Here’s some of the code…

    <div ng-controller="UsersController as vm">
      <div class="container">
        <div class="col-md-3 col-md-offset-9 dropdown-meditation">
          <div class="button-group">
            <select class="btn btn-default btn-md dropdown-toggle dropdown-text"
              ng-model="vm.option">
              <option value="" disabled selected>Sort Events By</option>
              <option value="date">By Date (Asc)</option>
              <option value="-date">By Date (Desc)</option>
              <option value="meditation.name">By Meditation</option>
              <option value="minutes">By Minutes</option>
            </select>
          </div>
        </div>
        <br>
        <div ng-repeat="event in vm.user.events | orderBy: vm.option">
          <p style="text-decoration: none">{{event.date | date: medium}} for {{event.minutes}} minutes &mdash;
        {{event.meditation.name}}</p>
        </div>
      </div>
    </div>

To give a bit of background, the information that’s being sorted here is an array of user events. The user has many events, which have the date, minutes, and a nested association of meditation, with id and name. This was all produced from an $http call to the back-end which was serialized into JSON by active model serializer – a very helpful gem for Rails.

Notice the dropdown and how we’re listing the options. The values of the options are bound to vm.options. All we have to do is declare vm.option = ” in the controller and when the dropdown is selected, vm.option will now be bound to the selected option. This will in turn set the filter in the ng-repeat. Notice the orderBy : vm.option that is declared in the repeat. This is the aforementioned built in magic of Angular. Whichever option is selected in the dropdown will change the orderBy option and effect how the list is ordered.

But what happens when you’re asked how it works. You know, like if someone asked you the what happens when you press the gas pedal in your car?  I don’t know, it just goes!

This is when it gets interesting. Getting your hands dirty.  Instead of using filter in the ng-repeat as above, we’re going to leave ng-repeat alone, with no orderBy filter! The hard work is going to be done in the controller. Check it.

The Hard Way

Here’s the  dropdown and ng-repeat…

    <div ng-controller="UsersController as vm">
      <div class="container">
        <div class="col-md-3 col-md-offset-9 dropdown-meditation">
          <div class="button-group">
            <select class="btn btn-default btn-lg dropdown-toggle dropdown-text"
                    type="button" 
                    ng-model="vm.selectedOption"
                    ng-change="vm.sortBy()" 
                    ng-options="option for option in vm.options">
              <option value="" disabled="" selected="">Sort Events By</option>
            </select>
          </div>
        </div>
        <br>
        <div ng-repeat="event in vm.user.events">
          <p style="text-decoration: none">{{event.date | date: medium}} for {{event.minutes}} minutes &mdash;
        {{event.meditation.name}}</p>
        </div>
      </div>
    </div>

In the dropdown above, ng-model is set to vm.selectedOption. Now whenever the user selects an option from the dropdown, the model is now bound to the option from ng-options. The options are declared in the controller which we’ll be taking a look at soon. The straw that stirs the drink is ng-change, which fires the vm.sortBy function in the controller. Let’s take a looksie.

.controller('UsersController', [function() {
      
      var vm = this;
      vm.sortBy = sortBy;
      vm.sortDate = sortDate;
      vm.sortMeditation = sortMeditation;
      vm.sortMinutes = sortMinutes;
      
      vm.options = ["Date (Asc)", "Date (Desc)", "Meditation", "Minutes (Asc)", "Minutes (Desc)"]

      function sortBy() {
        if (vm.selectedOption === vm.options[0]) {
          vm.sortDate();
        } else if (vm.selectedOption === vm.options[1]) {
          vm.sortDate();
          vm.user.events.reverse();
        } else if (vm.selectedOption === vm.options[2]) {
          vm.sortMeditation();
        } else if (vm.selectedOption === vm.options[3]) {
          vm.sortMinutes();
        } else if (vm.selectedOption === vm.options[4]) {
          vm.sortMinutes();
          vm.user.events.reverse();
        }
      };
 
// more code below

I like to bind my functions up top so I can see exactly what’s going on in the controller. This way I don’t have to search through hundreds of lines of code. Also, now we can just go ahead and write the functions as declarations, not expressions, which will save us from any hoisting headaches.

Like I said before, when the option is selected from the dropdown, that option is now assigned to vm.selectedOption and the vm.sortBy function fires. In the sortBy function, we’ll check to see which option was assigned to selectedOption and then fire a corresponding function to sort the user.events array. Here are the missing sort functions…

      function sortMinutes() {
        vm.user.events.sort((a, b) => parseInt(a.minutes) - parseInt(b.minutes))
      }

      function sortDate() {
        vm.user.events.sort((a, b) => (new Date(a.date)) - (new Date(b.date)));
      }

      function sortMeditation() {
        vm.user.events.sort((a, b) => {
          if (a.meditation.name < b.meditation.name) {
            return -1;
          } else if (a.meditation.name > b.meditation.name) {
            return 1;
          } else {
            let dateA = new Date(a.date);
            let dateB = new Date(b.date);

            if (dateA > dateB) {
              return 1;
            } else if (dateA < dateB) {
              return -1;
            } else {
              return 0;
            }
          }
// end of UsersController

Each function handles the data based on what the value is. Because the data in the user.events array are strings, we’ll have to convert them before sorting. The sortMinutes function is dealing with numbers which forces us to parseInt() the strings to integers. Same thing goes for the sortDate function, which we have to convert to a date before it can be sorted. Last is the sortMeditation function which takes the nested association – meditation – and sorts by name. Here we’re returning a negative 1 if the a is less, positive 1 if the b is less, and then instead of a zero if the names are the same, we’re using a sub-sort, which sorts by date.

Here’s a working plunker of the non-magic Angular filter. I think it’s cool to look under the hood sometimes and see what all the magic is about. You never know if you’re going to learn something new – I know I did! Leave a comment below if you have any questions. Thanks for reading!
 

 

 

Advertisements

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!

 

 

AngularJS & Rails: No Helpers:(

Angular is a front-end framework and is used for speed and asynchronous page loading (no page reloading on each request). Which means much of the helpers we rely upon in Rails are deemed useless. This can be hard to take for someone new to a front end framework like myself. But, alas, life must go on! The biggest obstacle for me so far is not being able to use the Flash Messages helper provided by Rails. What to do?

There are a few options out there on github that you can use. After trying a few different repos on Github which gave me a plethora of errors, I came across Toastr. It appealed to me right away because of it’s simplicity. I ran into one small bug (which was on my side), so I thought I might share.

The Set Up

screen-shot-2017-01-19-at-5-02-44-pm
First, to install Toastr, you can use Bower or npm. Next, copy the css and js files from the repo into your Asset Pipeline  – app/assets/stylesheets and app/assets/javascripts respectively. This will auto-rails-magically make the files available to use (as long as you //= require your trees *= in your application.js/css files). Now we can set up a factory in Angular.

The Chocolate Cheese Toaster Factory

screen-shot-2017-01-19-at-7-18-05-pm

Hey you…can you pass the butter, please?

Here we have set up a factory (we can just as easily use a service), which returns an object. Note that we also have access to other methods such as info and warning, but I only needed success and error for my project. When we call our toaster from a controller, we can give any text we’d like the user to see as an argument. I needed an error message telling a user that they couldn’t upvote a post more than once.


This Isn’t Going to Hurt, I Swear

We can then inject our toaster (factory) into any controller we’d like, then we have access to our success and error calls.

 

screen-shot-2017-01-19-at-8-20-51-pm
We’re injecting our toaster factory into our Main Controller. Now we can use it in any function we see fit. As I mentioned earlier, I needed to show the user an error if they tried to vote more than once on a post. By using a basic conditional statement with a some method on the array I could check for previous voters. I created a private function called voteChecker that checks the an upvoted posts user_id against the current_user $scope.user.id (I had to use inject the controller with Auth from the angular-devise gem for this).

screen-shot-2017-01-19-at-8-30-10-pm

By using the voteChecker function as the callback for the some method, we can check if any of the previous upvotes were from our current_user (I know, I know). However, the some method will return true if our array is empty, so we have to check that too. If the some method returns true && our upvoted_posts array’s length is not zero, then we have ourselves a two-timer and get to yell at them:) If, however, the challenge is passed, then we get to use our posts service (which I’m not going to delve into) to upvote our post. Very nice!

The Bug

I almost forgot! After doing everything above, I was getting a strange error:

TypeError: Cannot read property ‘extend’ of undefined

Crap! Well after a little Stack Overflowing it turned out that I was requiring jQuery after Angular in my application.js file. Once I put it before angular, everything was all good. NOTE – the same goes for if you’re including links or script tags referencing the angular and jQuery files from your application.html file. Well, all this toaster talk has made me quite hungry, so I think I’ll end here. If you ever need a flash message in your life and you’re using a front end framework, check out toastr!

 

Stumbling Through Angular: Routes!

AngularJS can be quite difficult to comprehend. Today, while trying to follow along Udemy’s Angular tutorial (hosted by Tony Alicea – it is great by the way!), I ran into some trouble with routing. Angular has built in routing that is very useful, but when things don’t work as you think they should, it can be frustrating. Every programmer in the world has been there. Although banging your head against the wall for a few hours can be quite fun, sometimes it’s best to go take a walk. When you come back, all of a sudden the problem is obvious. In my case, I was really trying to do something simple, but I just couldn’t get it to work.
screen-shot-2017-01-03-at-8-21-56-pm
Setting up the routing is fairly easy, once you  know the basic syntax of Angular. To the left is the file structure for my primitive app. I just wanted to have a main index page with two links. Simple right? Well a few hours later, yes…very. But if I can save anyone else a little time then it was all worth it. First off, we have to let Angular know that we want to use routing in our app. We can either use a link from Angularjs.org or we can download the node modules directly into our app, which I did. In both cases, we add it to our app with a script tag in index.html file. To download, in your terminal type:

‘npm install –save angular-route@1.6.1’

The numbers at the end represent the version of Angular you are running. If you’re not sure which version you’re running, open up package.json and you’ll find it in there.

Now we can add the script to our index.html file (it must be under your angular.js script!).

screen-shot-2017-01-03-at-8-39-31-pm

Note that these are all relative to my index.html file which is in the top of my file directory. Next, in app.js we let Angular know we want to rely on ngRoute. We do this in our module setup like so:

screen-shot-2017-01-03-at-8-32-02-pm

Here we are setting our module up and telling it to include ‘ngRoute’. Now we can use the routing capabilities. I set up two simple controllers to handle the two different links:

screen-shot-2017-01-03-at-8-50-42-pm

Here I’m using the array syntax which basically allows you to minify your files when you go to scale without any hiccups. But that’s for another blog post.  In the body of my index.html file I have two links and a div with a built in Angular directive.

‘ng-view’ tells Angular to look at the url screen-shot-2017-01-04-at-9-04-33-amand use the correct template that we set up with our routing. For this exercise I daisy chained all of my controllers and the like to my main module, but for bigger applications we’d have them in their own separate files. Now all we have to do is hook up our routing and we’re all set…

screen-shot-2017-01-04-at-10-16-54-am
So when we go to our main route, ‘/’, we’re expecting our ‘ng-view’ to summon up our main.html file with our $scope attached to our mainController. Welp, that didn’t work. I was getting a ‘404 (Not Found)’ error in my console. My main.html file couldn’t be found.

After searching stackoverflow.com for a  while I thought I found my answer. You can check out this stackoverflow exchange to see what I thought might be going on. Here’s the gist of it…

screen-shot-2017-01-04-at-10-50-12-am

So I tried a few of their solutions and also tried changing the templateUrl to no avail. After taking a much needed walk, I realized that I was giving the location of my html file relative to my app.js file and not my index.html file. So once I changed the templateUrl to reflect that, everything was groovy!

screen-shot-2017-01-04-at-10-27-00-am

Final app.js file

Unfortunately, something was still off when I clicked my links in the browser. The first link was working fine, but the second wasn’t doing anything. Well, technically it was changing the url in the browser, but nothing was going on in my terminal behind the scenes or changing on the web page. The main url is on the left and second link/url on the right.

Something funky was going on with the second link. There were a few characters that were being added – ‘#%2’ – before ‘second’. When I went directly to the ‘/second’ url, everything was cool – my page loaded up correctly. So what was the deal with the extra characters? What’s up Angular?!? I tried changing my href in index.html to include the bang before the ‘/second’. screen-shot-2017-01-04-at-10-55-15-am

And it worked! YAY! I still didn’t know what the heck was going on, but it worked! I thought that Angular, by default, adds a hash to url’s, which is why I included them in the first place. I didn’t know about the bang though. It’s called a hashbang, by the way. There are ways to beautify url’s in Angular, but I’ll leave that to more seasoned Angularites. I hope this will help someone out there! May your journey be smooth and remember – everyone stumbles! Just get back up and keep trekking#!

The Long and Winding Road

Guy-with-Antenna-on-Head-30t0fud1wcjfjrtrysmw3k

Loud and clear!

We are human beings! As humans, we get to create things! Awe-inspiring things! What sets us apart from the rest of the species that we share the Earth with, is this great gift. It seems like anything we can imagine, we can create. Amazing! The world is full of dreamers and creators. Put them together and quite frankly, magic happens. From architecture to art, music to computers. We stick our antennae up into the ether and connect with something we can’t explain. Somehow we process that signal and transmit it out into the world. The things that we come up with are astonishing! Of course, some of those things are only possible with the tools we are given.

 

Ada_lovelace

Hi there! I’m Ada

If you were born a hundred years ago, it would be impossible to write any type of software program. Maybe there were a few people around with advanced antenna that received signals for something like that back then, but there was no way to output that signal to the world in which they were born. It would have been considered science fiction. Well we are now living in that science fiction; and it is no longer fiction. Technology has quantum leaped humanity into unexplored territory; the timing could not have been better. You can say that technology (the industrial revolution, which made technology possible) has caused the crisis we face with global warming. The two are almost mimicking each other in their exponential growth. There is a race going on. A race for survival. Surely we can’t keep doing what we are doing and expect the same result. That, my friend, is the definition of insanity.

Speaking of insanity, when I was around four years old I realized that I had my very own personal jukebox in my head. There always seemed to be a song being played that only I could hear. When I realized these songs were not songs that I actually heard before, but something coming through my own personal antenna, I knew that other people should hear them too. What fun is it to keep these things to oneself? We are taught at an early age to share. So I decided that this was my reason for being on planet Earth. My burning desire. My passion. What is life for other than living for your passions.

What I’ve learned since is that not all passions are easy to live out, in monetary terms. With the subjective nature of the arts, there are so many moving parts that have to come together at just the right time to work out. I also learned (among many other things I hope) another thing: you can have more than one passion. If you’ve forgotten what your passions are or never thought you had any, a good place to look is your childhood. I always liked solving problems; from puzzles to building things. As soon as I got a new toy I would find the screws and take it apart. My Millennium Falcon and X-wing Fighters could testify.

 

Millennium Falcon

It was never easy putting this back together. Sorry Han!

When I first heard about coding bootcamps, they intrigued me. I never considered software development as a career. I’ve had computers around me most of my life, but I was always a mere user, never looking under the hood to see how all the magic happened (pretty much like my car – sure I can drive it, but I don’t have a clue how the engine is running). I still remember coming home to see my dad setting up our first one.

Screen Shot 2016-05-18 at 10.05.03 AM

Atari made computers?!?!

It was an Atari 800. Yep, that Atari. They were more than just a gaming console. Fast forward 30 or so years, I started reading about all of these people changing their lives by learning to code. I immediately started to use the vast resources on the internet to see if this was something I could get into. To my great surprise, I loved it. Which is no surprise if you think about it. Music is all about taking the abstract and reifying it into an audible form. Where as coding is all about getting comfortable in the abstract – levels and levels deep in abstractions. The inspiration I get by the amazing things people are doing with it is infecting everything I do. There is a mountain of things that I have to learn, and it seems the learning will never stop. Now I sit looking up at this steep mountain. With my hiking shoes tied tight, I am ready to climb. One step at a time with humility and awe.

I do believe that technology will be key to saving our collective asses. I hope to be a part of the solution.