rails render partial after ajax get request - ruby-on-rails

I use Jvectormap into my Rails 3 app.
When I click on it, I want to hide the map and visualize a partial with information about the selected country.
I handle the map click and I send a GET req to the server.
users.js.coffee.erb
$ ->
$("#world-map").vectorMap
onRegionClick: (event, code) ->
$('#world-map').hide(1000)
$.get('/users/statistics', {code: code});
$('#statistics').show(1000)
users_controller.rb
def statistics
#country = Country.find_by_two_letter_code((params[:code]).downcase)
render :nothing => true
end
this is the response
Started GET "/users/statistics?code=ET" for 127.0.0.1 at 2013-06-02 17:54:49 +0200
Processing by UsersController#statistics as /
Parameters: {"code"=>"ET"}
Country Load (0.2ms) SELECT "countries".* FROM "countries" WHERE
"countries"."two_letter_code" = 'et' LIMIT 1
Rendered text template (0.0ms)
Completed 200 OK in 2ms (Views: 0.6ms | ActiveRecord: 0.2ms)
and the view
show.html.erb
<p> <div id="world-map" style="width: 620px; height: 300px"></div>
<div id="statistics" style="width: 620px; height: 300px; display:none;">
<section>
<%= render partial:'statistics', :locals => {:country => #country} %>
</section>
</div>
</p>
_statistics.html.erb
<%= link_to '<i class="icon-remove"></i>'.html_safe%>
<%=country%>
<%=#country%>
Now, everything works, but the partial doesn't visualize the country value.
I don't undestand if I have to re-render the partial after the ajax get request, and how.
I've tried to change the controller in another way, but the behavior is the same.
def statistics
#country = Country.find_by_two_letter_code((params[:code]).downcase)
render 'users/_statistics'
end

i think that since you don't need a page refresh and just need to display the details the country, you can use ajax.
show.html
<p> <div id="world-map" style="width: 620px; height: 300px"></div>
<div id="statistics" style="width: 620px; height: 300px; display:none;">
<section>
<div id="place_partial_here"></div>
</section>
</div>
</p>
in the controller
def statistics
#country = Country.find_by_two_letter_code((params[:code]).downcase)
respond_to do |format|
format.js
end
end
in statistics.js.haml #i am comfortable with haml.
$('#place_partial_here').replaceWith("#{escape_javascript(render partial: 'country_data', locals: {country: #country})}");
in the _country.html.haml
#place_partial_here #id is denoted in haml using hash
= country.name #display country name
= country.code #display code
the partial must have the view code 'inside' the id 'place_partial_here' so that it can be used in further ajax calls

have you tried this
render :partial => "statistics", :collection => #country

Related

Rails turbo_frame not being recognized

context testing rails-ujs functionality with Rails 7 turbo streams as a full replacement.
Class nesting is structured as follows:
class Cartitem < ApplicationRecord
belongs_to :article
belongs_to :cart
validates :quantity_ordered, numericality: { greater_than: 0 }, on: :create
validates :quantity_ordered, numericality: { greater_than_or_equal_to: 0 }, on: :update
end
A page of articles can potentially create cartitems, one per article.
Each article needs to be creatable, updatable, deletable (so it may be recreated for users who change their minds incessantly).
Accordingly, that page has a block for each article
<% articles.each do |article| %>
<%= render 'article', article: article %>
<% end %>
# article partial
<div>
<%= article.description_short %>
</div>
<div>
<%= turbo_frame_tag dom_id(article) do %>
<%= render 'cartitem_block', article: article %>
<% end %>
</div>
the partial then invokes a turbo frame:
<%= turbo_frame_tag dom_id(article) do %>
<%= form_with(model: #cartitem, local: false) do |f| %>
<%= f.number_field :quantity_ordered %>
<%= f.submit %>
<% end %>
<% end %>
The rendered HTML does call the dom_ids and renders as expected:
<turbo-frame id="article_1">
<form action="/cartitems" accept-charset="UTF-8" data-remote="true" method="post"><input type="hidden" name="authenticity_token" value="sgABxVmZX0TDditdtwuGrgG4t9fkdfFMg02tpDnfgX3ch_IqD_YGoFJroE4u3y9t-bdLfGAyXZBUyJe04RBqtQ" autocomplete="off">
<input min="1" type="number" name="cartitem[quantity_ordered]" id="cartitem_quantity_ordered">
<button name="button" type="submit" ></button>
</form>
</turbo-frame>
[...]
<turbo-frame id="article_5">
<form action="/cartitems" accept-charset="UTF-8" data-remote="true" method="post"><input type="hidden" name="authenticity_token" value="..." autocomplete="off">
<input min="1" type="number" name="cartitem[quantity_ordered]" id="cartitem_quantity_ordered">
<button name="button" type="submit" ></button>
</form>
</turbo-frame>
The controller:
def create
#article = Article.find(params[:cartitem][:article_id])
price_ordered = #article.sell_price * params[:cartitem][:quantity_ordered].to_d
#cartitem = Cartitem.create!(article_id: #article.id, quantity_ordered: params[:cartitem][:quantity_ordered].to_d, price_ordered: price_ordered, cart_id: params[:cartitem][:cart_id].to_i)
respond_to do |format|
if #cartitem.save
format.turbo_stream
format.html { redirect_to cartitem_url(#cartitem), notice: "Cartitem was successfully created." }
else
format.turbo_stream
format.html { render :new, status: :unprocessable_entity }
end
end
end
If #cartitem is valid, the process runs and renders as expected:
Rendered cartitems/_cartitem.html.erb (Duration: 1.0ms | Allocations: 953)
Rendered cartitems/create.turbo_stream.erb (Duration: 1.3ms | Allocations: 1082)
However upon submitting with an empty value, to test the validation, an unexpected result occurs:
Started POST "/cartitems" for 127.0.0.1 at 2022-01-22 08:24:38 +0100
Processing by CartitemsController#create as TURBO_STREAM
Parameters: {"authenticity_token"=>"[FILTERED]", "cartitem"=>{"price_ordered"=>"3.41", "cart_id"=>"1", "article_id"=>"1", "fulfilled"=>"false", "quantity_ordered"=>""}}
[...]
TRANSACTION (0.2ms) ROLLBACK
TRANSACTION (0.2ms) ROLLBACK
↳ app/controllers/cartitems_controller.rb:28:in `create'
Completed 422 Unprocessable Entity in 11ms (ActiveRecord: 1.1ms | Allocations: 4874)
ActiveRecord::RecordInvalid (Validation failed: Quantity ordered must be greater than 0):
As expected, the transaction rolls back. However, the response is an Unprocessable Entity.
and the return is not what the create.turbo_stream.erb is stated to do, i.e. if the cartitem is nil re-render the partial.
*note: the else portion of the respond_to block was tried both with and without a reference to turbo_stream with identical result.
<% if #cartitem %>
<%= turbo_stream.replace "article_#{article.id}", #cartitem %>
<% else %>
<%= render 'cartitem_block', article: #article %>
<% end %>
# _cartitem partial:
<%= turbo_frame_tag dom_id(#cartitem.article), class: 'fade-text' do %>
<%= form_with(url: update_q_cartitem_path(#cartitem), local: false, method: :patch) do |form| %>
<%= form.number_field :quantity_ordered, value: number_with_precision(#cartitem.quantity_ordered, precision: 0), min: 0, style: 'width: 69px; display: inline-block;' %>
<%= button_tag(style: 'display: inline-block;') do %>
<p class='button warning' style='margin-top: 10px;'><%= fa_icon('recycle', class: 'fa 3x') %></p>
<% end %>
<% end %>
<% end %>
Instead, the xhr response returned is the HTML page of an error returned when in development mode.
The browser console in addition complains with
Response has no matching <turbo-frame id="article_1"> element
which is untrue as the HTML still has a lingering:
<turbo-frame id="article_1"></turbo-frame>
What needs to be done to properly error handle the case, render the form partial in its rightfully recognized place?
The condition where params submitted do not create a valid object should be explicit, particularly as the situation depends on a parent object:
else
format.turbo_stream { render turbo_stream: turbo_stream.replace( #article, partial: 'carts/cartitem_block', locals: { article: #article } ) }
end
otherwise the turbo_stream insists on trying to render the create turbo_stream variant which, in one case is calling a NULL object #cartitem and thus fails and is not able to proceed.
In your controller, you save the cartitem twice. The first instance (create!) throws an exception and stops the processing. Instead of
#cartitem = Cartitem.create!(blaa blaa)
Try
#cartitem = Cartitem.new(blaa blaa)
Now the object will get validated at the #cartitem.save step, and your turbo_stream file should load.
Also, and not related to the error, you could use strong parameters to make the controller action tidier and improve security.

Rails/AJAX: Partial not displaying?

Background (changed after correspondance with #TomLord):
I am building a simple site with only a index page and no subpages. The index page has a "search" field that takes input (id) and sends a GET request to a RESTful API running in the background. The response from the API is JSON {"market price": "4306057.0", "retail price": "4995000.0"}.
I want to display this result on the index page, together with the search field. However, when I press the button the result is not displayed anywhere.
Code:
index.html.erb
<section id="search">
<div class="container">
<%= form_tag({controller: "model_request", action: "result"}, method: "get", remote: true) do %>
<%= label_tag(:q, "Search for") %>
<%= text_field_tag(:q, "913384637") %>
<%= submit_tag("Get the price!") %>
<% end %>
</div>
</section>
<section id="display_result">
<div class="container">
</div>
</section>
And the modelrequest_controller.rb looks like this:
class ModelrequestController < ApplicationController
def result
id = params['q'].capitalize
response = RestClient::Request.execute(
method: :get,
url: "http://0.0.0.0:80/?id=#{id}")
#result = JSON.parse response
respond_to do |format|
format.js {render layout: false}
end
end
end
My routes.rb
Rails.application.routes.draw do
root 'index#index'
match "/modelrequest", to: "modelrequest#result", via: 'get'
end
The javascript for results looks like this:
result.js.erb
$("#display_result").html("<%= escape_javascript(render partial: 'modelrequest/result', locals: { result: #result } ) %>");
And the simple _result.html.erb for displaying the partial is
<div id="display_result">
<%= #result %>
</div>
Output:
Started GET "/model_request?utf8=%E2%9C%93&q=913384637&commit=Get%20the%20price!" for 127.0.0.1 at 2018-12-19 20:55:33 +0100
Processing by ModelRequestController#result as JS
Parameters: {"utf8"=>"✓", "q"=>"913384637", "commit"=>"Get the price!"}
Rendering model_request/result.js.erb
Rendered model_request/_result.html.erb (0.8ms)
Rendered model_request/result.js.erb (6.8ms)
Completed 200 OK in 383ms (Views: 11.6ms | ActiveRecord: 0.0ms)
In a "normal" rails site, a search form works as follows -
You define a form in the view. This will, by default, POST data to an endpoint - but you can also do this via a GET request. In your case, you originally chose:
form_tag("/search", method: "get")
This will send the form data to GET /search, synchronously, and therefore perform a full page reload.
This is a perfectly valid thing to do. However, you wanted to remain on the index page - therefore the request needs to be asynchronous:
form_tag("/search", method: "get", remote: true)
...But now, that endpoint needs to do something different. Instead of rendering a new page, it needs to partially update the current page.
The standard approach for this in a rails application is to do something along the lines of:
# In the controller action, e.g. SearchController
def show
#results = # ...
respond_to do |format|
format.js {render layout: false}
end
end
# In the view, e.g. `search/show.js.erb`
$("#display_result").html("<%= escape_javascript(render partial: 'results', locals: { results: #results } ) %>");
# In an HTML partial, e.g. `search/_results.html.erb`
<% results.each do %>
...
The key idea is that on your main page (index.html.erb?), you must have a container to display the results. Then, rather than rendering a new page, you are merely rendering an HTML update inside that container.
This is, of course, not the only way of doing it. Modern websites will often instead fetch JSON data for a search result and then determine how to display that via JavaScript.
However, the above approach is the most "minimal" change from your current design - without needing to add additional design patterns, you can leave most of your existing haml/erb templates as-is, and just add a little code to render the results as a partial.

rails 5.1: pass variable to partial through ajax

I'm new to rails and experimenting. After I managed to do this, I was now trying to do the following:
when clicking on a link for a setting's name, it's description should be rendered in a partial on the same page. I managed to render the partial, but only if I put some static text in it. For some reason I'm not able to pass the #fbs variable and thus to select the descriptions. I tried many different suggestions on other similar questions but I really don't get how I should do it. I'm using ruby 2.4.0 and rails 5.1
Here is my _settingsfb.html.erb (which is a partial of index.html.erb and which contains the Link_to)
<div class="container fluid">
<div class="row">
<!-- settings links -->
<div class="col-md-4 ">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">Settings</h3>
</div>
<div class="panel-body">
<% #fbs.each do |fbs| %>
<ul>
<li> <%= link_to fbs.name, controller: 'settingsfb', action: 'show', id: fbs.id,remote: true %></li>
</ul><% end %>
</div>
</div>
</div>
<!-- setting description -->
<div class="col-md-4">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">Description</h3>
</div>
<div class="panel-body">
<div id="desc">
<!-- here goes the description partial -->
</div>
</div>
</div>
</div>
here the settingsfb_controller (the relevant part)
class SettingsfbController < ApplicationController
before_action :set_setting, only: [:show, :edit, :update, :destroy]
# GET /settings
# GET /settings.json
def index
#settings = Setting.all
#fbs = Setting.where("Sns = 1")
end
# GET /settings/1
# GET /settings/1.json
def show
respond_to do |format|
format.js {render layout: false}
end
end
the show.js.erb
$("#desc").html("<%= j render 'settingsfb/show' %>");
and the _show partial
<%= #fbs.name %>
this is what I get when I click on a the link
Started GET "/settingsfb/show?id=1" for 127.0.0.1 at 2017-05-14 17:08:34 +0200
Processing by SettingsfbController#show as JS
Parameters: {"id"=>"1"}
Setting Load (0.3ms) SELECT "settings".* FROM "settings" WHERE "settings"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
Rendering settingsfb/show.js.erb
Rendered settingsfb/_show.html.erb (2.6ms)
Rendered settingsfb/show.js.erb (3.8ms)
Completed 500 Internal Server Error in 6ms (ActiveRecord: 0.3ms)
ActionView::Template::Error (undefined method `name' for nil:NilClass):
1: <%= #fbs.name %>
app/views/settingsfb/_show.html.erb:1:in `_app_views_settingsfb__show_html_erb___617047289144576595_70310163068960'
app/views/settingsfb/show.js.erb:1:in `_app_views_settingsfb_show_js_erb___3755597588635490219_70310163079560'
app/controllers/settingsfb_controller.rb:17:in `block (2 levels) in show'
app/controllers/settingsfb_controller.rb:16:in `show'
any Ideas?
You should initialize #fbs before rendering after show action. So add #fbs = Setting.find(params[:id]) at the beginning of the show method.
The error indicates there is no #fbs variable. You're calling the settingsFbShow partial from the show.js.erb view but not passing any variables. Try
$("#desc").html("<%= j render 'settingsfb/show', locals: {fbs: fbs} %>");
Then in create.js.erb
<%= fbs.name %>

Poltergeist and phantom render partial with respond_to in controller

I have a problem executing my rails app feature files headless. I use phantom as webkit and poltergeist as javascript driver.
My feature starts like this:
#javascript
Scenario: Create a customer
Given I am on the "/customers" page
When I click on element having id "new_customer"
Then I should see "Customer data"
The message I get running it is:
Then I should see "Customer data"
features/step_definitions/feature_steps.rb:16
expected to find text "Customer data" in "LOGOTIMAX :: adminTicketsProjectsCustomersUsermanagementProfileSettingsLog out Listing Customers Show entriesSearch: Id Name 1 Heine 2 IKEA 3 Coca Cola 4 Lufthansa AG 5 Daimler AGShowing 1 to 5 of 5 entriesPrevious1Next New Customer TIMAX - Ruby on Rails application by UniCorp BLOGJIRA" (RSpec::Expectations::ExpectationNotMetError)
It seems as if the partial where the text "Customer data" should be displayed does not get rendered by phantom. When I do the same by hand in my browser, it works.
Here are the other corresponding files:
customers/index.html.erb:
<h1>Listing Customers</h1>
<div class="grid">
<div class="row cells3 ">
<div class="cell" id="customers_list">
<%= render partial: 'customers/list', locals: { customers: #customers } %>
</div>
<div class="cell colspan2 " id="current_customer"></div>
</div>
</div>
<br>
<%= link_to 'New Customer', new_customer_path, class: "button", id: "new_customer", remote: true %>
customers_controller.rb (new)
def new
respond_to do |format|
format.js {render layout: false}
end
end
customers/new.html.erb
<h1>New Customer</h1>
<p>Customer data</p>
<%= render 'form' %>
customers/new.js.erb
$("#current_customer").html("<%= escape_javascript(render partial: 'customers/new' ) %>");

Google Maps InfoBox causing ActionController::UnknownFormat error in Rails app

I'm building a Rails app which includes a map with markers (aka "plots" in the app). When a marker/plot is clicked an infobox appears next to the marker. I'm trying to add a feature where clicking an infobox renders a partial alongside the map with further details relevant to that specific plot.
I'm using Gmaps4Rails and the feature described above works fine when implemented with Google's standard Infowindows but if I try to use the InfoBox plugin, so I can have all the styling benefits of InfoBoxes, I get an error.
ActionController::UnknownFormat
Screen grab of rails error message here
Looking at the log it appears the request is being correctly sent as JS with Infowindows but for some reason when using the InfoBox plugin it is being sent as HTML.
Log message with Infowindow is this...
Started GET "/plots/1/plotdetails" for ::1 at 2016-05-21 13:50:02
+0100 Processing by PlotsController#plotdetails as JS Parameters: {"id"=>"1"} Plot Load (0.3ms) SELECT "plots".* FROM "plots" WHERE
"plots"."id" = $1 LIMIT 1 [["id", 1]] Rendered
plots/_plotdetails.html.erb (0.1ms) Rendered
plots/plotdetails.js.erb (4.0ms) Completed 200 OK in 17ms (Views:
14.4ms | ActiveRecord: 0.3ms)
Log message with InfoBox plugin is below...
Started GET "/plots/2/plotdetails" for ::1 at 2016-05-17 18:52:38
+0100 Processing by PlotsController#plotdetails as HTML Parameters: {"id"=>"2"} Plot Load (0.3ms) SELECT "plots".* FROM "plots" WHERE
"plots"."id" = $1 LIMIT 1 [["id", 2]] Completed 406 Not Acceptable in
3ms (ActiveRecord: 0.3ms)
ActionController::UnknownFormat (ActionController::UnknownFormat):
app/controllers/plots_controller.rb:95:in `plotdetails'
I can't find a solution so am thinking about trying to implement this with JQuery AJAX instead but I can't help but feel this should be fixable. Any ideas would be much appreciated.
routes.rb
Rails.application.routes.draw do
resources :architects
resources :plots do
get :plotdetails, :on => :member
end
root 'plots#map'
plots_controller.rb (there is more code in the controller but this seems to be the relevant part)
def plotdetails
#plot = Plot.find(params[:id])
respond_to do |format|
format.js { render :layout => false }
end
end
views/plots/map.html.erb
<%= render 'partials/menu' %>
<div id="plotdetails"></div>
<div id="map"></div>
<script type="text/javascript">
buildMap(<%=raw #hash.to_json %>);
</script>
views/plots/_infobox.html.erb
<%= link_to plotdetails_plot_path(:id => plot.id), :remote => true do %>
<div id="infobox">
<div id="infobox-img">
<img src="<%= plot.img_link %>">
</div>
<div id="infobox-text">
<h3><%= plot.title %></h3>
<p>Architect: <%= plot.architect.name %></p>
<p><%= plot.address %></p>
</div>
</div>
<% end %>
views/plots/_plotdetails.html.erb
<div>
<ul>
<li>plot.title</li>
<li>plot.details</li>
</ul>
</div>
views/plots/plotdetails.js.erb
$( "#plotdetails" ).html( "<%= escape_javascript( render( :partial => "plotdetails", :locals => { :plot => #plot} ) ) %>" );
Since respond_to has been removed from rails 4.2 to a individual gem i will recommend you to use gem 'responders'
Please checkout the link given below for more details..
Why is respond_with being removed from rails 4.2 into it's own gem?

Resources