I'm using Rails + Devise for my log in and sign-up, but the vast majority of my front-end uses angular routing. When someone attempts to go to a specific page without being logged in I want them to be redirected after a successful log-in. Usually this would be a simple matter of storing the path in the session and modifying the after_sign_in_path_for method. However, I cannot seem to find a Rails method that will effectively get the actual path. I'm confident I could do this through JS and then store it as a cookie (or Angular really), but I'd prefer to keep it within Rails. Is there a method I'm missing that will give me the actual path including the /#/whatever/whatever?
It seems that the below should work:
request.original_url
based on this (http://api.rubyonrails.org/classes/ActionDispatch/Request.html)
But it isn't returning the hashtag or following parameters.
Perhaps due to what this person says (AngularJS routing without the hash '#')
The # is an old browser shortcircuit which doesn't fire the request, which allows many js frameworks to build their own clientside rerouting on top of that.
Is there a clean way to gather the full path within Rails or do I need to go into Angular/JS?
I had the same problem but with Backbone. The issue is that browsers don't send the anchor part of the URL to the server, so Rails can't handle this issue, only the JS in the front-end.
I am posting the workaround that worked for me.
In the sign in page add this javascript snippet (most likely in app/views/sessions/new.html.haml)
:javascript
// keeping the hash route into a cookie to retrieve it after sign in redirect
cname = 'hash_route'
cvalue = window.location.hash
// checking if the hash value is blank, don't overwrite the cookie
// this check covers the case of user giving wrong credentials in login
// then successfully login within the expiration time of the cookie
// other cases that aren't supported are relatively rare
if(cvalue.length){
var d = new Date();
d.setTime(d.getTime() + (3*60*1000)); // enough time to log in
var expires = "expires="+d.toGMTString();
document.cookie = cname + "=" + cvalue + "; " + expires;
}
In the SessionsController#create (or you can overwrite the Devise method "after_sign_in_path_for")
def create
# ------- Devise code --------
self.resource = warden.authenticate(auth_options)
set_flash_message(:notice, :signed_in) if is_navigational_format?
sign_in(resource_name, resource)
# ------- This is the code --------
# handling the case of redirecting to a link that had a hash route
location = after_sign_in_path_for(resource)
if cookies[:hash_route].present?
location+= cookies[:hash_route]
cookies.delete :hash_route
end
respond_with resource, :location => location
end
I hope this helps.
Thanks a lot @bero for this beautiful answer. It works perfectly.
I'll add my 2 cents with the following variations:
<!-- app/views/sessions/new.html.erb: -->
<script type="text/javascript">
cname = 'hash_route'
// Added a .replace(/^#/, '') here to store '/hash_path' instead of '#/hash_path'
cvalue = window.location.hash.replace(/^#/, '');
if(cvalue.length){
var d = new Date();
d.setTime(d.getTime() + (3*60*1000)); // enough time to log in
var expires = "expires="+d.toGMTString();
document.cookie = cname + "=" + cvalue + "; " + expires;
}
</script>
And in the ruby side:
# application_controller.rb
def after_sign_in_path_for(resource)
if cookies[:hash_route].present?
anchor = cookies[:hash_route]
cookies.delete :hash_route
end
webpages_app_path(anchor: anchor)
end
I prefer to use the path_helper
with anchor
and to override after_sign_in_path_for
(which you probably already had), instead of customizing Devise controllers.
But the whole thing is the same.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With