Rails - ability to link directly to views that are rendered via ajax - ruby-on-rails

Looking for the best way to implement this. Currently I have a "show" page for Users - that shows all of a users' pictures.
def show
#user = User.find(params[:id])
#pictrues = #user.pictures
end
On that page, I have various tabs. When a user clicks on one of those tabs, an ajax call renders a view... particularly, it updates a partial that was previously showing all of a users pictures (and an additional partial for statistics). For the "show comments" example, it updates the partial with all of the pictures a user has commented on:
def show_comments
#user = User.find(params[:id])
#pictures = #user.picture_comments.map{ |p| p.picture }.uniq
respond_to do |format|
format.html
format.js
end
end
The show_comments.js.erb file looks like:
$("#user_content_container").html("<%= escape_javascript render(partial: 'shared/pictures', pictures: #pictures) %>");
$("div.user_header").html("<h4>Comments</h4><br/>");
$("#stat_container").html("<%= escape_javascript render(partial: 'shared/comment_stats', pictures: #pictures) %>");
What I want to do, is to keep the current functionality of the page. But also be able to link directly to the views that are rendered via ajax. For example, have a link on another page that goes directly to the users "show" page, as it is when the "comments" tab is clicked on.
I have a few ideas, but an not sure what the "cleanest" way of doing this would be. Let me know if you need any additional clarification, b/c I'm honestly having as difficult time wording this question, as I am in finding the best way to implement this!

This sounds like something that you might be able to solve using turbolinks. If you can update your app to use Rails 4, you get this bundled in. Otherwise, you can use the gem. For more information on how to do this, watch the railscast on turbolinks.
If you don't want to or can't use them, you could also try passing params in the url, and check for them when the page is loaded. You could use the params to modify the page in the same way that it would be modified after the AJAX call.

Related

redirect_to(:back) same part of page?

I have a social feed.
If the user scrolls down a lot it is annoying to the user that by liking/commenting he is redirected to the top of the page instead of in the same part of the page to where he had scrolled to.
Is there any way to do this? Otherwise I'll just use paginate to make the pages smaller, which isn't ideal because that also takes away from user friendliness.
class ActivitiesController < ApplicationController
def index
#activities = Activity.order("created_at desc").where(user_id: current_user.following_ids)
end
def show
redirect_to(:back)
end
end
I've been on a roll with questions please check them out if you have time :)
Assuming that is being redirected to the top of the page because the page is being reloaded after a comment/favorite, you could try performing these actions using ajax instead.
This way, the page won't reload and you can modify the DOM to reflect the user's actions with javascript.
Here's some more information on ajax in rails:
http://guides.rubyonrails.org/working_with_javascript_in_rails.html
First generate anchors in your page by giving them ids. For example:
<div id="activity5">
..
</div>
Then in your controller, redirect to that part by using an anchor option:
redirect_to(request.env["HTTP_REFERER"] + '#activity5')
Note: redirect_to(:back) is the same as redirect_to(request.env["HTTP_REFERER"])
Having said that, using Javascript and AJAX is probably a better option.

Rails - how to pass created record from the new form to a redirected page

I think this is a pretty simple question but nothing I've read has answered my question directly:
I have a new products page with a standard form. After successfully submitting the form, I redirect to a custom controller action and view called "thanks".
On the "thanks" page, I want to be able to print the name of the product just created and possibly some other attributes.
How do I pass the object just created into my new action? Right now the controller looks like this:
def create
#product = Product.new(params[:product])
if #product.save
flash[:notice] = "Successfully created Product."
redirect_to thanks_path
else
render :action => 'new'
end
end
def thanks
end
You can't send object through redirect.
There are three ways to solve your problem:
Render the 'thanks' template directly(not action #thanks)
render 'thanks' # thanks template
You can send whatever instance variable to this template directly. #thanks is no longer needed in this case.
Drawback: The url won't be changed.
Convey messages through session
If you want to show certain messages, you can prepare it in #create and send it through session or flash(part of session actually). flash is better as you don't need to clear it manually.
Note: You may want to use ActiveRecord as session storage if the message size is big, otherwise you'll meet CookiesOverflow by default setting.
Send very simple message through session say obj_id
Similar to #2 but I thinks this is better than #2. In #thanks, you can construct complex message according to if obj_id is present, what is the id and then find related data through db.
You have two fairly decent options.
First, you could adjust the thanks_path route to take an id parameter, and call it like redirect_to thanks_path(#product). Then you can call it up in your thank you method like any standard show method. It might be worth mentioning that if you are going to be displaying sensitive information on the thank you screen, you may want to use a random uuid, instead of an id, to look up the product.
A better way might be to not redirect at all, but rather adjust your view from simply drawing the form to something like this:
<% if #product && !#product.new_record %>
THANK YOU MESSAGE GOES HERE
<% else %>
EXISTING FORM GOES HERE
<% end %>

Rails: How to render pages using AJAX and maintain DRY

I have a calendar page that shows a person's routine for an entire month. At the top, I have button for the previous and next months. When those are clicked, I make an AJAX call to the change_date action which updates the dates and finds the new routines. When the change_date.js.erb is called, do I have to write the code to change the content for everyday's actions again? This seems to be violating DRY.
Is there any way I can reuse the partials I used to populate the page on initial load to repopulate the page with the new values?
Of course, there is. Simply use render as you did in your html templates. Usually a setup that supports ajax and regular calls looks something like this (of course names are wildly guessed):
# controller
def change_date
#dates = Date.where(:date => params[:date])
respond_to do |format|
format.html
format.js
end
end
# change_date.html.erb
<ul id="calendar">
<%= render #dates %>
</ul>
# change_date.js.erb
$("#calendar").html("<%= escape_javascript(render #dates) %>");
# _date.html.erb
<li><%= date.date %></li>
So you see, you can simply use the same partial on both scenarios.
And just another suggestion: Depending on your setup it may be more restful to not have another method in the controller, but to use the show method which operates depending on a query param in this case...
I would load the person's routine via ajax when the page is first loaded as well.
I would encapsulate it in a js function and call that function when the page is first loaded and everytime the month is changed.

Redirecting to a random page while maintaining browser history

I'm trying to implement a "random page" link, which will render to the user a random article from the database. I've tried two separate (but similar) approaches, in both of which I route the URL "/random" to the "random" method in the ArticleController. Here's the first:
def random
offset = rand(Article.published.size)
#article = Article.published.offset(offset).first
render :action => 'show'
end
This works for serving random articles, but there are two issues: First, the URL doesn't update to the correct article, so users can't copy the link or bookmark the article; second, the previously viewed random articles don't show up in the browser's back button's history (i.e. pressing "Back" brings the user back to the page they were on before clicking "random" for the first time).
The second approach substituted render with redirect_to:
def random
offset = rand(Article.published.size)
#article = Article.published.offset(offset).first
redirect_to #article
end
This fixes the first issue - it's a redirect, so the browser is actually redirected to the appropriate URL for the randomly selected article (so it's available for copying/bookmarking). However, the problem with the Back button still remains. Moreover, it feels a bit wrong to co-opt an HTTP Redirect for something like this.
What would be the best way to go about serving random articles, while displaying the correct URL for the article and also maintaining a browser history chain?
Why don't you make actually a LINK for random article?
Helper:
def random_article_link
random_article = Article.find_by_sql("SELECT 1 FROM articles ORDER BY RANDOM() LIMIT 1") # for MySql RAND()
link_to "Random Article", random_article
end
In your approach you can not change URL string on a fly on a controller level. Only on routing level using Constraints.
Umm what about
#article = Article.find(rand(Article.count))
redirect_to #article
I wanted to do something similar and was also having trouble. Here is the solution I came up with:
In your controller you don't need a new page articles/random. I'm assuming you want the link on your article page which would be the show action in your controller.
def show
#article = Article.find(params[:id]
#random_article = Article.order('random()').first
end
Then in your view show.html.erb file
<%= link_to "Random Article", #random_article %>
Expanding on Chris's comment a bit - it's possible create a link within the view which will generate a random id for the next link (if you have some reason to do this...that's up to you).
<%= link_to 'Show me a random thing', thing_path(rand(1..4)) %>
You could drop this wherever and when clicked it'll simply route to a random page. Of course you'll want to change the number to reflect actual :id 's

Rails - display results of index and show actions via ajax

I have a very simple Post resource with two actions, index and show. My template contains a sidebar with links to each previous post. I want the sidebar links to display their content (i.e. the results of the "show" action) via ajax
I know there are lots of excellent tuts that show you how to create a form that submits with ajax but this time I want to use it to display the contents of my index and show actions without page referesh
. Are there any decent tutorials out there that give tips on how to do this?
I reckon I need to create a show.js.erb file, and tell my index action to respond to js but I'm a little bit stuck getting any further. I don't quite know what to put in controller's show action or show.js.erb - it's a little difficult to visualise what I need to do
PS - using rails 3.0.7, jquery-1.5
Got this working, it was very simple in the end.
Step 1 - add :remote => true to links in sidebar
#application.html.haml
%nav#sidebar
- for post in #posts
= link_to post.title, post_path, :remote => true
%div#main
= yield
Step 2 - tell your controller to respond to JS on the show action
def show
#post = Post.find(params[:id])
#posts=Post.all # needed for sidebar, probably better to use a cell for this
respond_to do |format|
format.html # show.html.erb
format.js # show.js.erb
end
end
Step 3 - Create _post.html.haml
# _post.html.haml
%article.post
= sanitize post.body
Step 4 - Create show.js.erb and replace the html in the #main div with the contents of the _post partial (that we created in step 3)
# show.js.erb
$("#main").html("<%= escape_javascript(render #post) %>");
Now all the content is passed via ajax and it's working fine.
I don't have a full answer at hand, because I'm relatively new to this, too.
But, I would start by taking a look at JQuery's get() method. You should be able to use that to call the index or show method. Those methods should return the html that you want to display in the non-sidebar section of the page. You can use get()'s callback handler to place that HTML into the appropriate div on the page.
Sorry that this is a little vague, but if you play with this I bet you'll figure it out.

Resources