Rails Rendering a Single Partial after Page Load - ruby-on-rails

I have a partial page in my application named _dashboard.html.erb. Inside of the dashboard, I have a lot of calls to render different partials. One of those is the _calendar.html.erb partial, which makes a call to google calendar and pulls down a list of events. This load takes time and I want to have the _calendar partial load after the rest of the dashboard has loaded. It should be reiterated that I don't want this to be based on a click event and instead when the page loads.
pages_controller.rb
# Logged in to shift
def jump
#title = "#{User.find(session[:user_id]).firstname}'s Dashboard"
#tasks = Task.where(:completed => 0)
#questions = Question.where(:answered => 0)
serv = GCal4Ruby::Service.new
serv.authenticate("email","pass")
cal = GCal4Ruby::Calendar.find(serv, {:title => "Skidmore Center for Sex and Gender Relations"})
##events = GCal4Ruby::Event.find(serv, {'start-min' => Time.now.utc.xmlschema, 'start-max' => 5.days.from_now.utc.xmlschema })
#events = GCal4Ruby::Event.find(serv, {'start-min' => Time.parse("01/01/2011").utc.xmlschema, 'start-max' => Time.parse("06/01/2011").utc.xmlschema, :calendar => cal.id})
end
Jump is the page where I have the dashboard partial rendering. Here is the part of /app/views/layouts/_dashboard.html.erb that calls the calendar partial:
<div id="main-content">
<div class="post">
<h3>calendar</h3>
<%= render 'layouts/calendar' %>
</div>
</div>
And /app/views/layouts/_calendar.html.erb
<% #events.each do |event| %>
<% if event.start_time >= Time.now and event.start_time <= 5.days.from_now %><li><%= event.start_time.to_s(:short) %> <%= event.title %></li><% end %>
<% end %>
I think what you should keep in mind here is that, while I understand how AJAX works, I have little to no experience coding it by itself or in Rails. Any help here would be appreciated (And the more dumbed-down and explained the better!)
Thanks
Update: I used the async_partial gem but am now having issues with the partial pulling the events from google calendar. The partial works without the async_partial.

One of the way to handle this without a gem, which is quite common by the way, is to use some kind of event handler for the page load event. For instance, with jQuery, you can do :
// this get executed when jQuery is made available, which means when
// your document is ready.
$(function () {
// you replace the actual calendar content with the piece that is
// rendered with your partial
$("#your-calendar-div-id").replaceWith('<%= escape_javascript(render 'layouts/calendar') %>');
});

Related

Rails: Having a button in a view call a method in a controller, route not working

Hi I'm making a rails app that uses Zendesk API calls. I have a controller that uses two classes I defined
class TicketsController < ApplicationController
require 'ticket_fields'
require 'ticket_search'
def getTickets
#search_fields = SearchFields.new(client)
#tickets = TicketSearch.new(client)
end
def search_tickets
#ddcustomvalues = [params[:customer_company_id], params[:study_id], params[:type_id], params[:system_id]]
#tickets.search_tickets(ddcustomvalues)
end
end
One class SearchFields uses the api to load values I want to filter tickets by into arrays. My view then uses these values to populate drop down lists.
The other class TicketSearch looks like this.
class TicketSearch
attr_reader :tickets, :text
def initialize(client)
#text = "query"
#tickets = Array.new
client.tickets.all do |resource|
#tickets << resource
end
end
def search_tickets(custom_search_fields)
querystring = "type:ticket+tags:"
custom_search_fields.each_with_index do |field, index|
unless field == ""
if index ==0
querystring += "#{field}"
else
querystring += " #{field}"
end
end
end
#text = querystring
end
end
What I want to happen in my view is when a button is pressed it changes the value of #text to the querystring generated by the drop down list options that were selected. I'm currently doing this for testing to see if my querystring is correct and the button works. What I eventually want it to do is send the querystring to the ZenDesk Server and returns the tickets I filtered for. the #tickets array would then be replaced with the filtered tickets the server returned. Currently my button code looks like this.
<%= button_to 'Search', :action => 'search_tickets' %>
with all the route code I've tried I either get an error upon starting the page. Or when I press the button nothing happens and the #text being displayed in my view remains "query". Can someone help explain what I need to do I don't quite understand how routes work.
==================================================================================
Hey so I made the changes you suggested and did some reading up on AJAX and js and I think I'm almost at the answer my view now looks like this
<div id="test" >
<%= render partial: 'text', locals: { text: #tickets.text} %>
<div id="test" >
and I created a partial _text file that looks like this
<p> Query: <%=text%> </p>
and a js file search_tickets.js.erb
$("#test").html("<%= escape_javascript(render partial: 'text', locals: { text: #tickets.text } ) %>");
any idea what may be going wrong everything loads up okay but the text remains the same in the partial i set up when i hit the button still
the console outputs this after the button is hit
ActionController::RoutingError (No route matches [POST] "/tickets/search_tickets"):
so I guess it may actually be a routing error my route looks like this
resources :tickets do
collection do
put :search_tickets
end
end
and the form tag calling the path looks like this
<%= form_tag search_tickets_tickets_path, remote: :true do %>
<table>
<tr>
<td align = "left" valign="middle"> <font size = 4> Customer Company </font> </td>
<td align = "left" valign="middle">
<%= select_tag "customer_company_id", options_for_select(#search_fields.customer_companies), :prompt => "Select One" %>
</td>
</tr>
......
<tr>
<td> </td>
<td align = "left" valign="middle">
<%= submit_tag "Search" %>
</td>
</tr>
</table>
<% end %>
==================================================================================
(Update)
I think I fixed my last problem by changing my form tag to this
<%= form_tag search_tickets_tickets_path(#tickets), method: :put, remote: :true do%>
however now I get this error from the terminal after I hit the button
NoMethodError (undefined method search_ticket' for nil:NilClass):
app/controllers/tickets_controller.rb:15:insearch_tickets'
how would I pass #tickets as a parameter through my route because clearly its not accessible by search_tickets right now as its giving a nil class error.
Variables
when a button is pressed it changes the value of #text to the querystring generated
It looks to me like you're confused with the stateless nature of Rails - in that, just because a view has been rendered doesn't mean the values / variables are still available for use.
It was mentioned in the comments that it seems you're basing a lot on experience with other frameworks / programming patterns. The best way to describe your solution is that Rails has to "refresh" all your variables / values each time it processes a request; consequently meaning that if you send a button request - you'll have to perform the request as if it were the first one
Ajax
The bottom line is that you need to use an ajax request to pull this off.
To do this, you'll be be best creating a form (not just a button_to), as this will give you the ability to send as many params as you want. You should use form_tag:
#config/routes.rb
resources :tickets do
collection do
get :search_tickets
end
end
#view
<%= form_tag tickets_search_tickets_path, remote: :true do %>
... #-> fields for your params
<%= submit_tag "Search" %>
<% end %>
This will give you the ability to define the following in your controller:
#app/controllers/tickets_controller.rb
Class TicketsController < ApplicationController
def search_tickets
#ddcustomvalues = [params[:customer_company_id], params[:study_id], params[:type_id], params[:system_id]]
#tickets.search_tickets(ddcustomvalues)
respond_to do |format|
format.js #-> loads /views/tickets/search_tickets.js.erb
format.html
end
end
end
#app/views/tickets/tickets_search.js.erb
//JS here to manipulate your original page
Requests
The bottom line here is that if you want to "manipulate" your view without refreshing, unlike "native" application frameworks, where you can rely on a persistent state, with Rails, you basically have to construct the request from scratch (IE passing all the params required for the method to run)

Render some content on Rails view

I am new to Rails, so I'm not sure if this is the correct way to go about doing this. I have a view that contains an AJAX link for a number of checkboxes in the form of
<% #row_headers.each do |row_header| %>
<% row_header_ = row_header.gsub(" ", "-") %>
<%= check_box_tag row_header_, row_header, params.key?(row_header_),
:data => { :remote => true,
:url => url_for(
controller: :web_pages,
action: :administratorswgraph} %> <%= row_header %>
<% end %>
<% end %>
This is being called from the :administratorswgraph view. Later on in the file I have
<% if not #swChart.nil? %>
<div id="swChart"></div>
<%= render_chart(#swChart, 'swChart') %>
<% end %>
Where render_chart is from the GoogleVisualr library. On the controller I have
def administratorswgraph
headers = []
#row_headers.each_with_index do |row_header, i|
if params.key? row_header.gsub(" ", "-")
headers.push i
end
end
if headers.empty?
#swChart = nil
else
#swChart = MakeSiteChart(headers, 580, 480)
end
end
Where the MakeSiteChart function returns a GoogleVisualr object based on the checkboxes. What I want is that for every time the checkbox's state is changed a new chart is made and shown. I can tell from my debugger, that indeed <%= render_chart(#swChart, 'swChart') %> is getting called in the view whenever a checkbox's state is changed, however the display in the browser is never updated. How do I get the display in the browser to show the chart?
Edit
I was able to get control of the ajax event by using the following method
$('#<%= #row_headers[0].gsub(" ", "-") %>').on('ajax:success', function(event, xhr, settings) {
alert("HERE")
});
For testing purposes I'm only hooking up the first checkbox. However, I'm not sure how to parse the arguments, how to get the chart, and how to insert it back into the DOM.
If your controller is RESTful then web_pages#administratorswgraph can answer to html and ajax with separate view (say administratorswgraph.html.erb and administratorswgraph.js.erb).
Place in administratorswgraph.js.erb all your logic responsible for replacing the entire chart... I think its enough to place <%= render_chart(#swChart, 'swChart') %> there, but Im not sure.
assuming that you are using jQuery and so jQuery-UJS, you can hook into the AJAX-Callbacks and replace the chart in your dom with the response that came from the server: https://github.com/rails/jquery-ujs/wiki/ajax
unfortunately, there is no good example for this on guides.rubyonrails.org...
After messing around with this a lot I was able to come up with a satisfactory solution. I used the following JavaScript within the view
<script type="text/javascript" charset="utf-8" id ="myScript">
<% #row_headers.each do |row_header| %>
$('#<%= row_header.gsub(" ", "-") %>').bind('ajax:success', function(event, data, status, xhr) {
var scripts = $(data).filter('script')
var chartScript;
scripts.each(function(index, Element){
if (Element.text.indexOf("swChart") != -1 && Element.id != "myScript") {
chartScript = Element.text;
}
});
jQuery.globalEval(chartScript)
});
<% end %>
</script>
This allowed me to extract the JavaScript that needed to be run after the ajax request came back and I was able to run it.

How to make partial_counter work with pagination

I have a blogging application in which User has_many posts. I am using pagination with Booststrap. How can I make the partial_count method work with pagination? Currently, the count resets on every page instead of carrying over across pages.
posts_controller.rb
def index
#posts = Post.order("created_at desc").paginate(page: params[:page], :per_page => 12)
end
views/posts/index.html.erb
<%= render #posts %>
<%= will_paginate %>
views/posts/_post.html.erb
<%= post_counter +1%>
<%= post.name %>
The counter works fine on the first page. However, all subsequent pages also start with "1". How can I make subsequent pages start with (number of pages * 12 + 1) instead?
Thanks for your feedback!
Use #posts.offset to get the proper counter initialisation.

How to update a 'div' content after that ActiveRecord values are changed without reload the page?

I am using Ruby on Rails 3 with Prototype/Scriptaculous and I am trying to update a 'div' content after that ActiveRecord values are changed, without reload the page.
In the "edit.html.erb" I have:
...
<div id="test_id">
<%= #account.name.to_s %>
</div>
<%= link_to_function( "Make test" ) do |page|
page.replace_html :test_id, #account.name.to_s
end %>
...
Before clicking on "Make test" I update the '#account.name' value, even via AJAX. Then, clicking on "Make test", the template doesn't changes.
These are steps:
I show the page
I update '#account.name' via AJAX
I click on "Make test"
'div' with 'id="test_id"' doesn't change!
What am I doing wrong?
P.S.: I think that #account is not reloaded in the page, also if his values are changed in the database. If so, what should I do?
I have seen the Railscasts "AJAX with RJS" and I followed an example in that (it is like the my), but it doesn't work for me.
If you have rendered edit.html.erb when the value of #account.name is "George", the HTML will remain the same (with the value "George") until you refresh it. Your call to link_to_function is, on page load, rendering an html link that calls javascript that replaces the inner html of the "test_id" div with 'George'. Unless you replace that HTML, it will always replace the inner html of that div with 'George'.
It's hard to recommend a solution without knowing exactly what you'd like to do...
updated to have more detail:
If you are making an AJAX call that changes the value of the account name on the server to "Fred", and want that change to appear on the page, you should refresh the parts of the page that use that value in that same AJAX call (that same controller action).
Your link_to_function generates HTML like this (if #account.name was 'George' when the page was rendered):
<a onclick="try { Element.update("test_id", "George"); ... >Make test</a>
It is not an ajax call, it is just a link that executes javascript. If you want to make it an ajax call that finds the latest value of the account name and refreshes the test_id div, do something like this:
<%# you need prototype included, but it should probably be in application.rhtml %>
<%= javascript_include_tag "prototype.js" %>
<div id="test_id">
<%= #account.name.to_s %>
</div>
<%= link_to_remote "Make test", :url => { :controller => "account", :action => "change_name" } %>
The 'Make test' link will now perform an AJAX call to the 'change_name' method in the 'account' controller. This method would look something like this:
def change_name
# here you would load the account from the db; I'm cheating
#account = Account.new(:name => "Fred")
render :update do |page|
page.replace_html :test_id, :text => #account.name.to_s
end
end

RoR facebox for prototype

I've got facebox working using the following tags in my rails app
<%= link_to 'test' some_path, :rel => 'facebox' %>
Now that works perfect if the page is already loaded.
However, I have ajax updates on certain parts of the page which contain new links like the one shown above.
When you click the links from an ajax update facebox doesn't work follows on to the template.
I believe since the page doesn't refresh the source code is the same and :rel => 'facebox' doesn't work.
Does anyone have any advice on how I can get this to work without refreshing the page?
I've tried this in a method in a controller.
render :update do |page|
page << "
facebox.loading();
facebox.reveal('text', null);
new Effect.Appear(facebox.facebox, {duration: .3});
"
end
However, for some reason in chrome and IE the facebox appears for a brief second and then disappears.
Any advice? I've been banging my head off the wall all day.
Thank you
All you need to do, is ensure that you're making a javascript call to facebox somewhere in the view of your AJAX response: arguably the best location would be in the layout file. Just make sure that you include the following:
#app/views/layouts/ajax.html.erb
#...snip...
<% javascript_tag :defer => 'defer' do -%>
jQuery(document).ready(function($) {
$('a[rel*=facebox]').facebox();
})
<% end -%>
or with prototype (untested):
#app/views/layouts/ajax.html.erb
#...snip...
<% javascript_tag :defer => 'defer' do -%>
document.observe("dom:loaded", function() {
$$('a[rel*="facebox"]').facebox();
});
<% end -%>
This will ensure that any new links that appear on the page, as part your AJAX updates, will be handled by facebox.

Resources