⚠️ This lesson is retired and might contain outdated information.

Rails Todo API Part 2

Brett Cassette
InstructorBrett Cassette
Share this video with your friends

Social Share Links

Send Tweet
Published 11 years ago
Updated 5 years ago

Expanding on Part 1, this lesson will look at how to bootstrap AngularJS in a rails application as part of the asset pipeline. With Angular available, you'll see how to communicate with the Rails API using $resource.

Werner Laude
Werner Laude
~ 11 years ago

Thanks for the nice video.. I got 1 error:

XMLHttpRequest cannot load http://localhost:3000/api/v1/todos.json. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://0.0.0.0:3000' is therefore not allowed access.

on my Mac must be: must be: var Todo = $resource('http://0.0.0.0:3000/api/v1/todos/:id.json', {id: '@id'}

Brett Cassette
Brett Cassetteinstructor
~ 11 years ago

Hey Werner- Looks like you've run into the Same-origin policy (http://en.wikipedia.org/wiki/Same_origin_policy), which I should have made more explicit in this video. But since we're in the comments section, we have enough time to describe the whole problem here:

The basic concept is described in the error message you received: by default, XHR requests are allowed to be made to the same host without any special dance. Javascript on the domain 127.0.0.1 may make requests of the host at 127.0.0.1, but if it makes a request of some other server, the server must explicitly set the Access-Control-Allow-Origin to allow the host making the request (check out CORS http://en.wikipedia.org/wiki/Cross-origin_resource_sharing).

There's a good reason this is confusing with Rails: 0.0.0.0:3000, 127.0.0.1:3000, and localhost:3000 will all route us to our Rails application (assuming your /etc/hosts file contains this line:

127.0.0.1	      localhost

which it does by default on your Mac, and you can check by running $ cat /etc/hosts).

localhost is just the name used to get to the reserved 127.0.0.1 IP address (the loopback interface for your computer, which will always loopback to your own machine). Just like any other host name in the DNS, localhost routes to an IP address that just happens to mean "my computer," unless you specifically deleted that line from your hosts file.

You can play around with setting any additional name you like to 127.0.0.1 (please don't delete the localhost resolution; it's important), and typing in yourname:3000 in your address bar with the Rails server running. If you end up at your Rails application, you'll know you've done this correctly, and it should help reinforce the purpose of etc/hosts, and also this next point:

0.0.0.0 is another reserved IP address that Rails uses by default. This special IP address means that the Rails app is listening on every available address on your machine (including 127.0.0.1, and any other IP addresses your machine might use). Since Rails is listening on all available addresses, calls to 127.0.0.1:3000, 0.0.0.0:3000, localhost:3000, and yourname:3000 will all take you to your Rails app (albeit, technically in this example they all end up actually referring to address 127.0.0.1:3000).

BUT even though these are take you to the same place, the algorithm used to calculate an origin under the Same-origin policy checks for the triple threat: protocol, host, and port (e.g. protocol => http, https, ftp, wss, etc.; host => 0.0.0.0, 127.0.0.0.1, or localhost; port => 3000, 8000, whatever).

In your case, you've used typed 0.0.0.0:3000 into your address bar, but told $resource to make a request to localhost:3000. They are the same host in that they're the same machine serving up the same application, but the host name disparity tricks the algorithm in this case, because these appear to be different hosts, thus giving you the error that you need to set the Access-Control-Allow-Origin header in Rails.

To see what I mean, try mixing up your hosts in $resource between 0.0.0.0, 127.0.0.1, and localhost, and then mixing them up in the address bar. You'll notice you end up with this error only when the address in your bar is different from the address used in $resource.

Make sense?

Bharat
Bharat
~ 11 years ago

Please ignore my earlier comment, I just saw Brett's reply. Nice explanation. Beware of the following omission from the video. The author uses 'lodash' library which is drop-in replacement for the underscore library. If you look at this bower.json file, you will see it there. Add it, and then do bower:install. This will install it. From there on, his code worked perfectly without any changes. Hope this helps.

Brett Cassette
Brett Cassetteinstructor
~ 11 years ago

I apologize Bharat; I should have mentioned that would be used in creating the controller off-screen. Thank you for pointing this out for other viewers!

Bharat
Bharat
~ 11 years ago

You are very welcome! Please keep the Rails and Angular Related videos coming :) This justifies my annual membership. I am a Ruby on Rails programmer who has learned javascript the hard way. I have developed a product in Ruby on Rails using backbone framework and am looking to replace it with Angular. So the more knowledge I get here in making Rails and Angular work together, the better. You do an excellent job of teaching. I just thought I might point this out for those who may not know this. Thank you.

Brett Cassette
Brett Cassetteinstructor
~ 11 years ago

It's a great thing for you to do; the livelier our community, the easier it is for relative newcomers to technologies to pick up certain levels of detail I have to gloss over in higher-level videos. This was a tricky one because we can't expect everyone on here to have a good working knowledge of Rails (this is an Angular site, after all).

Are there particular Rails + Angular topics you'd like to see discussed?

Werner Laude
Werner Laude
~ 11 years ago

Hi Brett && Bharat Thanks for the informative comments..helped a lot. I am also developing with Rails. I am sure Angular will have same importance in my 'dev life' like RoR, but I am still at the beginning. Avoided JS as much as I can.. ;-) But Angular looks like fun and well structured. Fits perfectly.

So these screencasts are a great help for me. Love to get more. Q: Brett, is the a reason for the nested routing api/v1/ ? Topic: Eager to get to know more about => $scope.todos = Todo.query();

p.p.s. How to get a pic in my account here?

Bharat
Bharat
~ 11 years ago

Hello Brett, Thanks for asking :) I had no idea that bowers was integrated with Rails with the gem that you used. It was an excellent source.

Here is my Christmas shopping list for Rails and Angular:

  1. Different ways of connecting Angular with Rails, e.g. ngResource (what you did here), $http, Restangular, angularjs-rails-resource, their pros and cons.

  2. Leveraging Angular with Rails using Angular Strap, Angular UI and other UI widgets. I use Bootstrap a lot and anything that fuses both Angular and Bootstrap is worth exploring, but of course, in a Ralis context.

  3. Complementing Angular with other libraries e.g. Lodash (or Underscore), Sugar JS, Moment JS etc, but again, in a Rails context :)

  4. How to map your Backbone knowledge to Angular. Or even better, how to make them work together in a Rails application.

This sort of thing becomes immediately useful to a Rails programmer like me who is constantly being bombarded by the users for more interactivity in the application.

Just a few suggestions :)

As I said, keep them coming :)

Bharat
Bharat
~ 11 years ago

Hello Werner, Go to http://en.gravatar.com/ and post your picture there. It will show up in most of the places including here :) Bharat

shawn
shawn
~ 10 years ago

Hello,

Thanks Bharat. I would also like to add that you need to make sure to add it to the application.js manifest file as well:

"//= require lodash"

Most people probably already know that, but it took me another 10 minutes of bonking my head against the wall before I figured that out.

Thanks for such a great video and for the helpful comments!

Chris
Chris
~ 10 years ago

using rails 4.1.1, mac

rake bower:install Bower not found! You can install Bower using Node and npm

Daniel Friis
Daniel Friis
~ 10 years ago

I'm having the same problem. Did you find a solution?

Chris
Chris
~ 10 years ago

I am sorry, this was few days ago, I did install with npm.

Brett Cassette
Brett Cassetteinstructor
~ 10 years ago

So I want to get to a computer to check out this gem and see if I can recreate the issue y'all are having, but I would recommend starting with brew remove bower && npm install -g bower.

Brett Cassette
Brett Cassetteinstructor
~ 10 years ago

After not reproducing this error locally on my Mac running Rails 4.1.1, I read through the bower-rails source. Here are some things to help debug:

  1. They're searching your $PATH for the bower executable.
  2. From the npm docs, a global install (with -g): puts stuff in /usr/local or wherever node is installed (for me this is actually /usr/local/bin). which node or whereis node should help you locate this.
  3. Bower should be in that same folder. You could also just check this directly with which bower or whereis bower, but if you cd `which node | sed s/node//` you should be see bower in that folder.
  4. That same folder should be in your $PATH
  5. All together that means that if you run this command, you should see the name of the folder where bower is installed: echo $PATH | tr ":" " " | grep `which node | sed s'/\/node//'`
  6. If you don't see it, you should add that folder to your $PATH. To do so, assuming you're using bash, you could add the line export PATH=$PATH:the path name you found in #2/3

Hope this helps guys.

shawn
shawn
~ 10 years ago

Hello,

Does anyone have experience deploying this app to heroku?

thanks.

Tobi
Tobi
~ 10 years ago

Thanks for a great tutorial! Does anyone have any information about best practices on how to create an independent AngularJS app as the front-end talking to a separate rails app as the API?

Brett Cassette
Brett Cassetteinstructor
~ 10 years ago

The whole connection to a server aspect happens in the Angular app, so the answer is...any way you want! Using $resource, restAngular, ngActiveResource--heck even straight $http. Just plug in the URL of your server.

The organizational approaches differ between each of those libraries, and if you go the $http route, it's probably because you have your own structure you want to pursue. I'd look at each library to see how it's commonly used if you want some ideas about that.

Brett Cassette
Brett Cassetteinstructor
~ 10 years ago

The whole connection to a server aspect happens in the Angular app, so the answer is...any way you want! Using $resource, restAngular, ngActiveResource--heck even straight $http. Just plug in the URL of your server.

The organizational approaches differ between each of those libraries, and if you go the $http route, it's probably because you have your own structure you want to pursue. I'd look at each library to see how it's commonly used if you want some ideas about that.

Tobi
Tobi
~ 10 years ago

Thanks Brett! I will take a look at those.

Tyler
Tyler
~ 10 years ago

Fantastic tutorial. Edit:: After messing with the code some more, the front-end code wasn't changing the completed value, so I updated it so that the boolean was changed. The only true issue I can't figure out is when I click on the active state, the ng-click isn't getting hit.

Markdown supported.
Become a member to join the discussionEnroll Today