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.
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?
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.
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!
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.
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?
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?
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:
Different ways of connecting Angular with Rails, e.g. ngResource (what you did here), $http, Restangular, angularjs-rails-resource, their pros and cons.
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.
Complementing Angular with other libraries e.g. Lodash (or Underscore), Sugar JS, Moment JS etc, but again, in a Rails context :)
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 :)
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
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!
using rails 4.1.1, mac
rake bower:install Bower not found! You can install Bower using Node and npm
I'm having the same problem. Did you find a solution?
I am sorry, this was few days ago, I did install with npm.
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
.
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:
$PATH
for the bower executable./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.which bower
or whereis bower
, but if you cd `which node | sed s/node//`
you should be see bower in that folder.$PATH
echo $PATH | tr ":" " " | grep `which node | sed s'/\/node//'`
$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.
Hello,
Does anyone have experience deploying this app to heroku?
thanks.
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?
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.
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.
Thanks Brett! I will take a look at those.
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.
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'}