This lesson is for PRO members.

Unlock this lesson NOW!
Already subscribed? sign in

Search Directive with Rails

7:35 Angular 1.x lesson by

Create a search directive with AngularJS and Rails. You'll want to take a look at the related Rails TODO API Part 1 and Rails TODO API Part 2. This is not a Rails how-to, so some knowledge there is expected to get up and running.

Get the Code Now
click to level up

egghead.io comment guidelines

Avatar
egghead.io

Create a search directive with AngularJS and Rails. You'll want to take a look at the related Rails TODO API Part 1 and Rails TODO API Part 2. This is not a Rails how-to, so some knowledge there is expected to get up and running.

Avatar
Bharat

Hello Brett,
Thank you for doing this screencast :) Please keep more Rails related stuff coming. One request that I have is if you can do a screencast on deploying an Angular Powered Rails application to Heroku with detailed sets of steps, that would be a great help.
Bharat

In reply to egghead.io
Avatar
Brett Shollenberger

Hey Bharat, should be no different from deploying to Heroku with Rails normally--and this article is great for that (https://devcenter.heroku.com/articles/getting-started-with-rails4). Maybe we could look at a general continuous deployment system that would work for all Angular users, since it seems that might be more of what you're looking for?

In reply to Bharat
Avatar
Bharat

Hello Brett,
Thanks for your response. Thing is: I am not sure what and how are the best practices for deploying Angular powered Rails applications to Heroku. I have used Heroku and continue to use it for the past 4 years and I really love the things that it does for me without requiring much attention. I have a few books on Rails and Angular and they all have different takes on how to deploy Angular/Rails apps. Here is a link to Fredric Dietz's sample app on Github:

https://github.com/fdietz/recipes-with-angular-js-examples/blob/master/chapter9/recipe1/contacts/app/assets/javascripts/angular/app.js.erb

for the following book on leanpub:

https://leanpub.com/recipes-with-angular-js

I do not understand as to why he has assets_path function to augment the Angular routes. I did not get a satisfactory response from the author so that kind of left me scratching my head. I was hoping that you could shed some light on what works for you since you know both Rails and Angular well. I am fine with Rails, but am a starter with Angular. Hence the request.

Regards,

Bharat

In reply to Brett Shollenberger
Avatar
Simon

Fantastic screencast!

Just curious what animation library you're using to animate the fade and slide effect on the table?

In reply to egghead.io
Avatar
Andrey

Where I can see code? github?

Avatar
Kamal

This video was very helpful! Thanks.

Hey, everyone. Today, I want to take a look at building a search directive that's going to query a back end and update a list that we have on our scope. To show you what I mean here, we've got a list of contacts.

We're only showing five of the contacts right now. Notice we only have one David in the list here. If I begin typing in here, we will query the back end and find David Black and, in fact, Ryan Davis as well.

Some people have asked me if you can do this using a filter. The answer is you can do this using a filter, but I would highly advise you not to do this using a filter. I'm going to show you why here.

This may look like we're using a filter for this. We are using a filter, but we're not using a filter in the way that you might be thinking. The way a filter would work is that it's going to run on every keystroke.

I'm going to simulate exactly what that's going to look like. Here, I actually have this functionality running on every keystroke, using jQuery. This is exactly the same thing that would happen if you were using a filter.

Now, if we reload this here and I type in "David," you see that's going to be a lot more chunky in our animation. It's going to actually run five times, once for every keystroke.

The way that we can resolve that is by adding a timeout. What happens when we run each keystroke when this function runs is we create a timeout, and that timeout's going to run after 200 milliseconds.

It's actually going to run when two conditions are true, when 200 milliseconds have passed and when the thread is free, which may not be exactly concurrent. They usually synch up fairly well.

When the thread is free and after 200 milliseconds has passed, that's when we're going to actually build the query and make the query on the back end. If that doesn't happen, if we get another keystroke before that point in time, we're actually going to cancel the timeout that exists already, that's assigned here to search timeout.

You see we have this in a higher-level scope so that it will be available to each of these key-ups. If that exists, we're just going to cancel it, and we're going to set a new one.

Now, if we re-save this here, what actually ends up happening is if I type pretty quickly, as people do, it's not going to fire each of my back-end requests, which are fairly long-running requests.

We get a smoother animation here. Instead of seeing that occur five times, it probably only happens once, depending on how quickly I'm typing. If I also pause while I'm typing, we want that to take a second and then offer a suggestion from our back end.

That works the way that we'd expect it to, now. That's why we don't want to use a filter to build this. That's why we want to use a directive to build this.

I want to show you some other options that I like to expose on filtering and searching functionality. The first thing that I like to expose here is the actual search value. If I haven't set any value, what I do here is I set this equal to any.

I set query.any equals the value that's passed in. That might be "query.any = David," in the case that I've been showing you so far. Otherwise, it's going to say, perhaps, "query.firstname = David," and that will get assigned in and get passed to the back end.

This is just something that I've set up my API to work with. Here, I'm going to set this to first name. Remember, we were getting Davis from the back end, which was a last name. Now, we're only going to get first names that start with "Dav."

That's an option that I like to expose, that's going to allow this directive to have a little bit more fine-grained functionality into how it controls this search. I'm going to delete that right now, but you know exactly what that does at this point.

The other things that I like to expose to make this reusable are the model that we're going to use. Here, I'm using the contact model. I'm actually exposing that on my controller scope so that we have access to it here.

This is probably what you'd expect to see, scope.contact equals the contact model. The reason I'm actually going and doing that is because if we use a $resource model, any one of those models will have a query method.

I actually just call down onto whatever that model is and call its query method. Then, what I'm going to do is actually take whatever is passed into scope.update and use that to update the list here.

That way, I can put any generic list in here. Here, I have scope.contacts as the list to update. If I had multiple lists on my scope, I could update any one of them here.

Another option that I like to pass in from my API...Again, this is just a little bit of functionality that I think is nice, from the API...is fuzzy equals true. If I set this to false or I fail to set it, then my directive's actually going to set it to false for me.

Now, if I type in "dav," I'm going to lose every one of these. I actually have to type in capital "D," "David," or else it won't be an exact match. What that's actually running on my back end, if I have fuzzy set to true, if I hop into my controllers here...

You don't have to know too much about Rails code. There's just a little SQL in here. It's actually going to chain together any of my queryable keys. I have first name, last name, and email as queryable.

It will say, "First name I like," which is case-insensitive. Lower-case "da" would match "David" in the first name column and also in the last name column, in "Davis." It'll build up a query here, for me. Just to take a little look under the hood as to how we expose that on the back end.

Those are the options that I like to expose to this search directive. Finally, I want to show you why we're actually using this filter, down here. This is a very basic filter. It's actually using this ng-model for the search.

This is going to filter the results that we have in place. If we actually take this off, we'll see that our other event really does only fire after 200 milliseconds have passed. Only once the user has finished typing something in does this fire to the back end, and then we filter down our results.

If, on the other hand, we go ahead and use this filter, refresh the page, and type in "David," we see that we start filtering down our result set immediately. After this timeout has fired, then we load in David Black, and we can add him after an appropriate amount of time has passed.

It feels really fluid. It feels very responsive because we get this response almost immediately, from Angular. That's a nice thing about these built-in filters, they allow us to complement a request like this search directive with live, in-place filtering.

There are lots of good ways to do this, and I'm sure there are lots of good questions you have about how to do this. The rest of the information that you'll need is in the show notes, such as this code. If you have additional questions, please leave them in the comments, and I'm happy to answer them for you. Take it easy, everyone.

HEY, QUICK QUESTION!
Joel's Head
Why are we asking?