When we use resource routing in Rails (paths like “/blogs/new” or “/blogs/2/edit”), we often use path variables such as new_blogs_path
. But, when when working in Javascript, we have to hardcode the the paths, i.e. “/blog/new”. In this article, we’ll cover JsRoutes, an awesome gem that lets us use a similar set of URL helpers in our Javascript code.
Key Takeaways
- JsRoutes is a gem that allows the use of Rails URL helpers in Javascript, eliminating the need to manually construct URLs.
- Setting up JsRoutes involves adding it to the Gemfile, running bundle install, and requiring JsRoutes in the Javascript file.
- JsRoutes can handle nested routes, making it easier to construct complex URLs. It also allows the use of the “to_param” function to improve SEO by generating more descriptive URLs.
- JsRoutes can be configured through an initializer, allowing options such as default URL format, excluding certain URLs, and specifying a prefix for all URLs.
Why do we need JsRoutes?
The idea is that you should not have to construct URLs manually in Javascript if a library can do it for you! This becomes evident as soon as you start doing nested routes. For example, consider the following routing configuration:
resources :posts do
resources :comments
end
If we want the path to edit the comment with an ID of 4 that is associated with the post with an ID of 1, it gives “/posts/1/comments/4/edit”, which is a mouthful. It is much easier to use the URL helpers supplied by Rails than construct that string in our Javascript.
Setup
If you have the asset pipeline working in Rails, setting up JsRoutes is incredibly easy. Just add the following to the Gemfile:
gem "js-routes"
Then, run bundle install
. In any Javascript file (often, in application.js
), you can require JsRoutes by adding the following at the top of file:
//= require js-routes
If, as you follow along with this article, you find that JsRoutes is not loading in your Javascript, you might have to clear the asset pipeline cache:
rake tmp:cache:clear
Now, you should have JsRoutes ready to go. Let’s jump in and see some of the basics.
The Basics
Say we’ve added a new resource-based route in config/routes.rb
:
resources :posts
Each post has a title field and a content field. In PostsController
, we might have something like this:
class PostsController < ApplicationController
...
def create
@post = Post.new(:title => params[:title],
:content => params[:content])
@post.save
respond_to do |format| do
#probably should render something more sensible in a
#real application.
format.json { render :json => "{}"}
end
end
...
end
Notice that we aren’t returning any errors that occur in saving the post as part of the response. This is a bad idea in practice, but we’ll let it slide since this example isn’t really concerned with the data returned from the server. In the Javascript, suppose that we have a nice form to create a new post and we want to submit that form with AJAX. Breaking out the jQuery:
$.post(path, {title: $("#title_field").val(), content: $("#content_field").val()}, function(response) {
//do something w/ the response
});
But, how do we get the value of path
? That’s where JsRoutes comes into play:
var path = Routes.posts_path({format: "json"});
If you tried to console.log
the value of that variable, you’d see "/paths.json"
, as expected. It can be a little bit difficult to remember which JsRoutes calls correspond to which actions within the controller (although the names are nearly the same as the corresponding Rails helpers). One way to get a quick refresher is to add:
console.log(Routes)
to one of your Javascript files and examine the output. Another way is with this handy table:
Controller#Action | JsRoutes function call |
---|---|
posts#index | Routes.posts_path() |
posts#new | Routes.new_post_path() |
posts#create | Routes.posts_path() |
posts#show | Routes.post_path(id) |
posts#edit | Routes.edit_post_path(id) |
posts#update | Routes.post_path(id) |
posts#destroy | Routes.post_path(id) |
Notice that JsRoutes consists of function calls which means, in Javascript, that we have to have the calling parentheses (i.e. “()”), regardless of whether or not we have an “id” argument. Also note that some of the paths for different actions are the same (e.g. the paths for posts#index and posts#create). For these, the correct action is called based on the HTTP verb used (e.g. GET vs. POST).
Nested Routes
Alright, let’s take a look at how to handle nested routes with JsRoutes. Fortunately, if you’ve had experience with the Rails URL helpers, you’ll feel right at home. Let’s say we have this route configuration:
resources :posts do
resources :comments
end
The URL helpers are a bit more wordy but the ideas are the same:
Controller#Action | JsRoutes function call |
---|---|
comments#index | Routes.post_comments_path(post_id) |
comments#new | Routes.new_post_comment_path(post_id) |
comments#create | Routes.post_comments_path(post_id) |
comments#show | Routes.post_comment_path(post_id, comment_id) |
comments#edit | Routes.edit_post_comment_path(post_id, comment_id) |
comments#update | Routes.update_post_comment_path(post_id) |
comments#destroy | Routes.post_path(id) |
As a rule, if you’re passing an ID for a specific resource, use the singular form of that resource (e.g. “post”) and if you’re not passing an ID, use the plural form (e.g. “comments”).
The Magic of “to_param”
If you have URLs like “/post/138” in your application, SEO might be a bit difficult because, from the URL, it isn’t possible to tell any information (e.g. title or content) about the post the URL is referencing. That’s why Rails gives us to_param
. We can see it in action in our Post model:
class Post < ActiveRecord::Base
#...
#maybe verify uniqueness of title
def to_param
title.gsub(/ /, '_').downcase
end
#...
end
If we have a post with the title “JSRoutes is Awesome”, then we can now refer to this post as /posts/jsroutes_is_awesome
which offers a slight improvement in SEO. JsRoutes lets you use “to_param” in order to generate paths as well. Let’s check out an example with a flat resource:
var path = Routes.post_path({to_param: "jsroutes_is_awesome"}, {format: "json"});
That produces the path /posts/jsroutes_is_awesome.json
. This does mean that if you have a copy of the title of the post in the Javascript, you will have to reimplement the to_param
logic in Javascript, which is unfortunate but, more or less, unavoidable.
Configuring JsRoutes
There are a number of ways to configure JsRoutes, which can be pretty useful for specific situations. You can manage the configuration through an initializer, so create a new one in config/initializers/jsroutes.rb. Here’s how the general format of the initializer works:
JsRoutes.setup do |config|
config.option = value
end
Basically, you have a bunch of possible configuration options that you can set. Let’s take a look at some of them. One of the most useful for me has been:
JsRoutes.setup do |config|
config.default_url_options = {:format => "json"}
end
That takes all of the url helpers and by default, tacks on a “.json” at the end. If you’re writing a one-page application where most of your HTTP requests are actually fired by Javascript, you almost never load HTML, as most of your data will be in JSON. In that case, it makes more sense to set the default format to be JSON.
Another important URL configuration option is :exclude
:
JsRoutes.setup do |config|
config.default_url_options = {:exclude => [/^admin$/]}
end
That will exclude any “admin”-associated URLs from the Route object given to the Javascript code. The idea is that you might not want all the URLs in your project to be exposed to the client (when designing your app, however, it is advisable to assume that an attacker does have this list of paths). So, with the exclude
key, we can omit certain paths using regular expressions.
Say you always want full paths (e.g. “http://example.com/posts”) rather than local paths (e.g. “/posts”). That’s easy too:
JsRoutes.setup do |config|
config.prefix = "http://example.com"
end
Note that there is no trailing slash.
Wrapping it Up
Writing Rails code in the modern, Javascript-driven web requires a lot of mental context switching. With JsRoutes, at least you don’t have to rethink the way you handle paths when you switch to the front-end parts of your codebase.
Frequently Asked Questions on Rails URL Helpers and JavaScript
How can I use Rails URL helpers in JavaScript?
Rails URL helpers can be used in JavaScript by using the js-routes gem. This gem generates JavaScript code that defines all your Rails routes as JavaScript helpers. You can include it in your application.js file and use it just like you would in your Rails views or controllers. For example, if you have a route named ‘new_user_path’, you can use it in JavaScript like this: Routes.new_user_path().
What is the purpose of Rails URL helpers?
Rails URL helpers are methods that create URLs based on your application’s routes. They are used to ensure that your application uses the correct URLs, even if the routes change. This makes your code more maintainable and less prone to errors. URL helpers can be used in views, controllers, and even in models, although their use in models is generally discouraged.
How can I access Rails URL helpers from a module?
To access Rails URL helpers from a module, you can include the Rails application routes URL helpers module in your module. Here is an example:module MyModule
include Rails.application.routes.url_helpers
def some_method
new_user_path
end
end
In this example, the ‘new_user_path’ URL helper is available in the ‘some_method’ method.
Can I use Rails URL helpers outside views and controllers?
Yes, Rails URL helpers can be used outside views and controllers. However, their use outside these areas is generally discouraged because it can lead to tight coupling between your models and your routing system. If you need to generate URLs in your models, consider using a presenter or a decorator instead.
How does Rails handle JavaScript?
Rails has a built-in system for handling JavaScript called the asset pipeline. The asset pipeline compiles and minifies your JavaScript files, making them faster to download and execute. It also allows you to write JavaScript in a more structured and modular way, using features like modules and classes.
How can I use JavaScript in my Rails views?
You can use JavaScript in your Rails views by including it in script tags. However, it’s generally better to put your JavaScript code in separate files and include them using the asset pipeline. This makes your code more maintainable and allows you to take advantage of features like minification and caching.
What is the js-routes gem and how does it work?
The js-routes gem is a library that generates JavaScript code that defines all your Rails routes as JavaScript helpers. It works by inspecting your Rails routes and generating corresponding JavaScript functions for each one. These functions can be used to generate URLs in your JavaScript code.
Can I use Rails URL helpers in my JavaScript tests?
Yes, you can use Rails URL helpers in your JavaScript tests. However, you will need to include the js-routes gem in your test environment and require it in your test files.
How can I customize the URLs generated by the js-routes gem?
The js-routes gem provides several options for customizing the URLs it generates. For example, you can specify a custom prefix for all URLs, or you can exclude certain routes from being generated.
How can I debug issues with Rails URL helpers in JavaScript?
Debugging issues with Rails URL helpers in JavaScript can be challenging because the error messages are often not very helpful. However, you can use tools like the browser’s developer console to inspect the generated JavaScript code and see what’s going wrong. You can also use the ‘console.log’ function to print out the values of variables and see what they are at different points in your code.
I'm a developer, math enthusiast and student.