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.
Related
I have a view like this (/app/views/projects/new.html.erb)
how should I use if else function to do this?
<p>
<%= f.label :payment_type, "payment_type" %>
<%= f.radio_button :payment_type, 1, checked: true %>(1)
<%= f.radio_button :payment_type, 2 %>(2)
</p>
if payment_type == 1
show these things
else(or payment_type == 2)
show these things
end
my controller save this views(/app/controllers/projects_controller.rb):
def new
#project = Project.new
end
def create
#project = Project.new(params.permit![:project])
end
how do I use it property
Javascript
You'll need to use Javascript to give you some front-end interactivity.
To give you some specifics - when you run a Rails application, the *rails" part of the system will run in the backend -- meaning that each time you render an "view", your controller is going to pull the data from your model, allowing Rails to create a pure HTML output for you.
The issue you have is that when you render this output, you cannot then invoke Rails functionality again for that call (look up how stateless technology works -- An example of a stateless protocol is HTTP, meaning that each request message can be understood in isolation.), meaning you need some way to manage the front-end area of your interface.
This is done with Javascript:
[Javascript] is most commonly used as part of web browsers, whose
implementations allow client-side scripts to interact with the user,
control the browser, communicate asynchronously, and alter the
document content that is displayed
--
This is how you'll handle it:
JSFiddle
"Bind" the radio inputs to a "change" event
When the change event is triggered, perform the business logic directly in your JS
Append the changes to the view (DOM)
Rails
Here's what you'll need to do specifically:
#app/assets/javascripts/application.js
$(document).on("change", 'input[type="radio"]', function(){
if($(this).val = "1") {
// something here
}else{
// something else here
};
});
#app/views/controller/your_view.html.erb
<%= f.label :payment_type, "payment_type" %>
<%= f.radio_button :payment_type, 1, checked: true %>(1)
<%= f.radio_button :payment_type, 2 %>(2)
--
Ajax
Further to this - if you wanted to return Rails-based business logic to your view, you'll want to use ajax. I won't go into too much detail with this, apart from saying that this will essentially send a "pseudo request" to your browser.
Here's how you'd set it up:
#config/routes.rb
resources :payments do
get :type, on: :collection #-> domain.com/payments/type
end
This custom method will allow you to perform the business logic you need:
#app/controllers/payments_controller.rb
class PaymentsController < ApplicationController
def type
type = params[:type]
if type == "1"
...
else
...
end
end
end
This will then give you the ability to crate an ajax call to this method:
#app/assets/javascripts/application.js
$(document).on("change", 'input[type="radio"]', function(){
$.ajax({
url: "payments/type",
data: { type: $(this).val() },
success: function(data) {
alert("Success");
}
});
});
<% if payment_type == 1 %>
these
<% else %>
those
<% end %>
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)
In rails, what kind of AJAX call would need to be made in order to create and modify resources. Say if i had a resource like this
Man(age: integer, country_from:string, residence:string)
Now how would this be made through an AJAX call (like sending post to my create function, how would parameters be sent in, how would my controllers be setup). Be detailed, my AJAX is very, very, very weak. (right now i have my Man made like rails generate scaffold Man age:int country_from:string ...)
PS
Im using rails 3
So I believe there are two sides to this: the javascript and the controller changes.
In your controller you need to ensure it can return json output (or xml or whatever your chosen ajax-y output is):
def man
# do your work
return_data = {}
# initialize your return data
respond_to do |format|
render :json => return_data.to_json, :layout => nil
end
end
There are many ways to generate your json output but basically you have to make sure it's in a shape that is easily consumed on the view javascript.
I use jQuery and here's the code to execute an ajax call:
function foo(some_param) {
$.ajax({
type: 'GET',
url: "/<controller>/man?FOO=" + some_params,
dataType: 'json',
success: handle_success,
error: handle_errors
}
function handle_success(data) {
# process return JSON. it's a javascript object corresponding to the shape
# of your JSON. If your json was a hash server side, it will be an 'object', etc
}
function handle_error(data) {
# handle error cases based upon failure in the infrastructure, not
# failure cases that you encounter due to input, etc.
}
You can tie the foo function to some button or onclick as you desire.
I am not sure this is complete enough. Let me know if you need more detail, etc.
Rails 3 can help by telling the form that you want it to be "remote" (ajax)
<%= form_for #man, :remote=>true do |f| %>
<div class="field">
<%= f.label :man %>
<%= f.text_field :man %>
</div>
<%= f.submit "Save", :disable_with=>"Saving..."%>
<% end %>
in your Controllers
class MansController < ApplicationController
respond_to :js, :html
def update
#man = Man.find(params[:id])
#man.update_attributes(params[:man])
respond_with #man
end
end
Then, you can have
/app/views/mans/update.js.erb
<% if #man.errors.any? %>
alert("This is javascript code that either renders the form in place of the other form");
<% else %>
alert("success!")
<% end %>
Note: Wherever I say "mans" above, it might be "men"
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') %>');
});
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.