August 2014
This post is for anyone trying to get AngularJS and Turbolinks to play well together.
Why would anyone want to do this you ask? Well, I can only speak for myself, but I think that AngularJS can be used within a traditional Rails app to enhance UX and create some highly interactive pages. The thing is, I wanted to be able to do this while still enjoying the snappy page loading from Turbolinks.
So, here goes nothing:
vendor/assets/javascripts/
in your rails application.Modify app/assets/javascripts/application.js
to include Angular
Create the file my_app.js
in app/assets/javascripts/
The Turbolinks custom event 'ready page:load'
will cause your app to be bootstrapped and compiled. Read more about the custom events in the Turbolinks documentation.
“Call angular.bootstrap to compile the element into an executable, bi-directionally bound application. Note: You should not use the ng-app directive when manually bootstrapping your app.” https://docs.angularjs.org/guide/bootstrap
This can be a nice and simple way to sort your Angular files. Feel free to use whichever directory structure you prefer.
Edit app/assets/javascripts/application.js
to include files & folders.
It’s important that my_app.js is loaded before the rest of the Angular files. Require tree will include all files/folders in the javascripts directory. You don’t have to worry about files being loaded twice, as the asset pipeline is smart enough to avoid that.
Modify the myApp config to setup the CSRF Token for valid $http requests.
Without this you cannot make valid requests to your Rails application.
app/assets/javascripts/controllers/test_ctrl.js
Add the controller and button to any erb template you’re viewing.
Click the button. Voila! You’re using Angular with Turbolinks!
Now, because you’re not building a single page application, you’re probably going to need a way to pass variables from your backend to your angular app.
Without Turbolinks, I would normally use ng-init to initialize these variables, and I’d use the angular.element().ready()
function to know when they had been set.
Unfortunately, with Turbolinks, you cannot rely on angular’s ready() method. In my testing, the event would fire before the variables had actually been set.
Luckily, there are a few work arounds!
A) Use $watch
with ng-init
: This is a quick & easy way to initialize variables in your controllers. By passing variables with ng-init, you can simply watch for a change in your controller, and then perform your controller initialization.
I like this for simple controllers, but it has a few downsides:
B) Set required values on your module: You can set values on your module, and inject them into your controllers. This completely avoids the need for ng-init at all. It will even throw errors in the event that you forget to set the value.
Here’s an example:
Credit goes to Court Ewing for showing me this method.
Well, that’s it for the tutorial! Hopefully this gives enough information for you to get started.
I realize most people will probably never want to mix AngularJS/Turbolinks, but I just wanted to go against the common advice of “Remove Turbolinks”, and this seemed like a fun thing to try.
If you’ve got any feedback or comments, please let me know!