Redirect_to another page in rails - javascript

In my application I have a set of entities. Now I want to build a search form on my start page that calls the action of controller a. If it finds more than one entitiy it shall show all the products if it finds exactly one product it should redirecto to another controller that loads the detailed information about the entity and shows it. In my first controller I do this by calling
if #entities.length==1
redirect_to show_path(:id=>#entities[0].id)
end
I would expect that now a new site is opened like /show?id=1234 but that does not happen. Instead the controller behind the entity path loads the detailed information of the entity but nothing is shown.
I get the following error:
ActionView::MissingTemplate (Missing template entities/show with {:formats=>[:js, :"*/*"], :handlers=>[:rjs, :rhtml, :rxml, :erb, :builder], :locale=>[:en, :en]} in view paths ..."):
How do I get the right page loaded, simply adding the show.js.erb to the entities folder makes the error disappear but the problem still remains that the show page is not shown.
EDIT:
render :update do |page|
page.redirect_to show_product_path(:id=>#entities[0].id)
end
this works but why? what is the difference?

I would suggest to rederect straight to object. Rails is smart enough to create route for your object.
if #entities.length==1
redirect_to #entities.first
end

I thnink
render :update do |page|
page.redirect_to show_product_path(:id=>#entities[0].id)
end
code is looking for a show action in the same controller, where as
render :update do |page|
page.redirect_to show_product_path(:id=>#entities[0].id)
end
is redirecting to products/show in products controller. I think you dont have a 'show' action in 'entities' controller thats why you are getting
ActionView::MissingTemplate (Missing template entities/show with {:formats=>[:js, :"*/*"], :handlers=>[:rjs, :rhtml, :rxml, :erb, :builder], :locale=>[:en, :en]} in view paths ..."):
With the default rails configuration it works as follows
in your controller
class EntitiesController < ApplicationController
def index
#will display all the products
**#you need to have a index.erb.html file as well**
#products = <Your product getting logic here>
end
def show
#display only one product
#you need to have a show.erb.html
#product = Product.find(params[:id])
end
end
So in you case you should redirect as
show_product_path with an id
and make sure you have show action defined in the controller
HTH
sameera

Related

Rails Controller - refresh page same as if I hit the refresh on my browser

I have inherited a Rails code base that I do not fully understand. We have a requirement to, when the user hits Submit, render ON THAT PAGE the set of validation failures the user put into the form. I cannot redirect to any other page - we must remain on the page which contains the form upon which they put the invalid input.
Here is my method
def tpr_bulk_update
updated, date_died = update_tprs #it returns 0,0 when there are validation fails
if updated == 0 && date_died == 0
flash.now[:notice] = 'A bunch of errors occurred'
#Here I need to refresh the page. I do not especially want to redirect.
#I want this to perform exactly the same as me hitting the refresh button on my browser.
#The initial form loaded via a very complicated codebase that I do not understand exactly.
#I do have available to me the params from the initial call - but it seems to me hitting refresh on
#the browser implicitly handles repassing to this method with the SAME PARAMS I CAME IN WITH....
#AND it shows my flash.now. So then, I want to refresh the page the same mechanism the browser uses,
#because this is what demonstrably meets my requirement
elsif !date_died
redirect_to tprs_index_vod_assets_path
else
flash[:notice] = "One or more TPR assets were not given valid future dates, so those invalid dates did not save".html_safe
redirect_to tprs_index_vod_assets_path
end
end
The issue is I see no way to do this. Perhaps the browser invoking refresh uses javascript that is impossible to inline in my rails controller?
redirect_to :back
fails on account that the set of params I came in with are not populated - it explodes.
respond_to do |format|
format.js {render inline: "location.reload();" }
end
My method does not output javascript, and neither will it ever output javascript - that is a requirement for the system.
I need whatever is equivalent to the refresh operation my browser (Chrome) performs when I press "Refresh". I want that to happen right after I set my flash.now message. How can I capture what Chrome/what hitting refresh actually does? And how can I perform it within my controller?
This is exactly what AJAX was designed for. By sending an asyncronous request you can send data to the server without reloading the page and update the page with the response. In Rails you can use Server Side Concerns to simply replace a chunk of contents with a rendered view.
Since you don't actually have an example of the form or controller this is a simplefied example that demonstrates the concept:
class ThingsController < ApplicationController
def new
#thing = Thing.new
end
def create
#thing = Thing.new(thing_params)
respond_to do |format|
if #thing.save
format.js
else
format.js { render :new }
end
end
end
end
end
# things/_form.html.erb
# remote: true is the default - its just added for extra clarity here
<%= form_with(model: #thing, remote: true, id: 'my-special-form') do |form| %>
<% if #thing.errors %>
# .. display the errors
<% end %>
# ...
<% end %>
# things/new.html.erb
<%= render partial: 'form' %>
// things/new.js.erb
// Since we want to extract the children of the form element
// we use DOMParser to create a fragment
const node = new DOMParser().parseFromString(
"<%= j render(partial: 'form') %>", // rails renders the template
"text/html"
);
// Find form on page and replace its contents
document.getElementById('my-special-form').innerHTML = node.querySelector('form').innerHTML;
// #todo set flash message
// things/create.js.erb
window.location = "<%= thing_path(#thing) %>";
The way that this works is that Rails UJS listens for submit events on any element with the data-remote attribute. Instead of the normal submit it will send an XHR request with the Content-Type: application/javascript header.
After rails is finished rendering and reponding with your js.erb view it is sent back to the client and Rails UJS takes the response and evals it by popping it into a script tag.

Rails 5.2. Rendering a js.erb partial from a helper method

I have a model called Question, and it has action create;
My goal is to display a flash message instantly, using a helper method (show_alert for example) when the instance is not valid.
question_controller.rb
def create
question = Question.new(question_params)
if question.save then
redirect_to show_question_path(question.id)
else
show_alert(:warning, question.errors)
end
end
application_controller.rb
helper_method :show_alert
def show_alert(type, message)
#type = type; #msg = message
respond_to do |format|
format.js { render :template => 'alert.js.erb'}
end
end
alert.js.erb
var div = $('<div></div>').addClass(`alert alert-${#type}`)
$('<ul></ul>').append( $('<li></li>').html(#msg)
div.append(ul)
$('#alerts').html(div)
But instead of displaying the flash, I get only the partial's code on the white screen.
see the screenshot
Since I've used respond_to I got another error: ActionController::UnknownFormat
I need the snippet of code in alert.js.erb to be executed, in order to render the flash, I think the trick is somewhere in the render function, but two hours of googling were just a waste of time.
Please help! Thank you in advance
ActionController::UnknownFormat error is showing up because the browser is sending HTML request to Rails server, but the respond_to block has only specified what to do in case of a javascript request from web server.
You will need to add a little bit of Ajax to achieve what you want. See this tutorial on Ajax. https://www.tutorialspoint.com/ruby-on-rails/rails-and-ajax.htm
Ajax will send a js request to browser in the background (i.e the browser will not refresh or show any signs of loading). This js request will be sent to Rails server and it will return the .js.erb file containing the script back to the browser. Now since this script was returned as a response to Ajax request by browser, the browser will already know that it is javascript that needs to be executed.
If you do not wish to implement Ajax, you have the alternate of doing something like this in your create controller:-
def create
question = Question.new(question_params)
if question.save then
redirect_to show_question_path(question.id)
else
redirect_to new_question_path(error: question.errors) #new_question_path is the action that displays the question form to the user
end
end
and then you can initialize an error variable in the action that displays the question form. e.g.
def new
#error=params[:error]
#rest of the code...
end
And then in somewhere in your new.html.erb (or whatever the html.erb file name is)
<script>
<% if #error %>
var div = $('<div></div>').addClass(`alert alert-<%= #type %>`)
$('<ul></ul>').append( $('<li></li>').html(<%= #msg %>)
div.append(ul)
$('#alerts').html(div)
<% end %>
// you might need to tweak the variable names in controller or the above code
</script>
(This code above may not be perfect. its just to give u an idea)
However this approach will not be as quick and beautiful as ajax because when the user will submit their question, the entire page will load again to display the error warning.
By default, all output from helpers is escaped. To show the HTMl as-is, you need to use the html_safe method (https://apidock.com/rails/v4.2.1/String/html_safe). See Using helpers in a view escapes the html?
I cannot be sure this without seeing your alert.js.erb but it could be that you need to use escape_javascript in your alert.js.erb
Something like (and I haven't tested this out) in your alert.js.erb
$('<%= escape_javascript("#{type} - #{msg}") %>').appendTo("#alert")
You can read more about it on Rails Guides - Working With Javascript in Rails
Hope this helps!

Rails 4: Passing a variable from controller to javascript

I'm new to Rails and have been confused by this problem for a while.
I'm looking to create a translation app. When a user clicks a link containing a word, I want the rails controller to get the translation through an external API call, save the word and translation, and then display the translation, all without refreshing the page.
The particular issue I have is getting some string (which will be the translation) to be passed from the controller to a js file, where it can then update the HTML to display the translation.
In the controller (texts_controller.rb):
def createword
#word = Word.new(word_params)
#word.save
#translation = "Some string"
respond_to do |format|
format.js
end
end
In the javascript (createword.js.erb)
console.log("done");
$(".message").html(<%= #translation %>);
When I delete the second line containing #translation, the javascript works just fine and logs "done" to the console. However, when I do include that line, none of it works.
How can I get this variable to the js, or if that is not possible, what is the best way to get it to the view?
I didn't try this, but adding some quotes will probably do what you want:
$(".message").text("<%= #translation %>");

Rails dynamic form remote true controller not responding

I am trying to establish a dynamic form on a contact's page. I would like on this page to be a link that says "add an email address" and then when I click on it, a form appears to enter email address.
So I used a link_to with remote true :
= link_to "Add an email", add_email_path, id:'link-remote-link', remote: true
In my controller i specified :
def add_email
render layout: false
end
But when I receive my response with listening on ajax:sucess, layout is still their in the variable. But I just want the form add_email.html.haml
In order to try to know if the code in my controller was executed, I tryed to put a creation of an object in it. Fact is that it was never created.
Never the less, rails console writes "Processing by ContactsController#add_email as JS
"
So...why is it not executed ?
Thank you :)
Layout
We set the layout in the application_controller to manage the ajax responses:
#app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
layout Proc.new { |controller| !controller.request.xhr? }
end
You may wish to try this - to see if it's the call in your controller which is rendering the layout.
Personally, I think your layout: false call is being overridden with some other element / part of your controller. I'd recommend checking to make sure this is the case
--
Controller
As you've stated, the case may be that your controller isn't being called, or processed correctly.
This could be caused by a number of issues, most notably from having incorrect routes, or some other dependency preventing the method from firing properly.
To clarify, I would make sure I have the following set up:
#config/routes.rb
resources :contacts do
get :add_email, on: :collection
end
#app/controllers/contacts_controller.rb
class ContactsController < ApplicationController
def add_email
...
end
end

Rendering partial with AJAX link_to in Rails

After completing Hartl's tutorial I'm trying to implement #replies for the Micropost model.
I wanted the reply button on a micropost to render the micropost form right there under said post a la twitter. I also wanted to pass the :micropost_id of said post to the reply so that I could later reference which post it was a reply to, again a la twitter.
I've been trying to implement some of that based on the answer to this question.
My microposts controller has
def reply_form
respond_to do |format|
format.js
end
end
The link in the view is
<%= link_to 'reply', 'shared/reply_form', remote: true, locals: { object: :id } %>
With my attempt there to pass on the micropost :id
After a post I have this for the partial to be rendered in:
<div id="ReplyContainer"></div>
I then have _reply_form.js.erb
$('#ReplyContainer').html('<%=j render partial: 'shared/reply_form', locals: { object: :id } %>')
It's not throwing errors but clicking the 'reply' link has no effect and does not render the _reply_form.html.erb partial.
My ultimate goal is you click reply, that renders the micropost form, with #username at the start of the message (derived from the micropost_id?) submitting that form then saves the new micropost including the micro post_id of the original post in the reply_to column that I've created in my microposts table. Any nudges in the right direction much appreciated. Don't want to be barking up the completely wrong tree. Thanks.
EDIT: I'm currently getting a routing error when clicking the reply link.
(No route matches [GET] "/shared/reply_form")
But as far as I can see everything is in the right place.
Ok, sorry, I've just read better your question and the problem is related to the wrong structure.
you have a controller with an action that will render the post
your post page will be composed by the "post" and many replies
each reply I guess is made of a partial (let's say a _reply.html.erb file).
The structure of the page will be then the following:
<div>
... my post here...
</div>
<div id='repliesContainer'>
<%- #replies..each do |id| -%>
<%= render :partial => "shared/reply", :locals => { :object => id} %>
<%- end -%>
</div>
And we call it post.html.erb.
This is will be your shared/_reply.html.erb file:
// note here that the id of the div contains the ID of the reply: each ID must be unique in the page....
<div id='ReplyContainer-#{object}'>
...reply content here...
</div>
Now, your post.js.erb file contains the following:
$('#repliesContainer').append("<%= escape_javascript( render :partial => 'shared/reply', :locals => { :object => id}) -%>");
The content of the append() function will be rendered on the server as a string from the partial.
You have several problems here:
As you discovered, the route to your reply form is incorrect. Until you get this fixed, you won't be able to debug the rest of the system. Run rake routes | grep reply_form to find the URL for it (it won't be under /shared most likely), then see what the response is for that URL. I bet it throws an error (see #2).
Your reply form file has the wrong name: _reply_form.js.erb is a partial but it needs to be 'reply_form.js.erb'. If you hit the correct route for that controller, you'll get a 'Missing Template' error because it is looking for a normal template, not a partial.
Finally, the reply_form.js.erb code needs to point to the shared reply_form partial, so if is really in shared/_reply_form.html.erb then the JS response should be rendered correctly.
Having said that...I really dislike sending Javascript back to the browser, in part because it makes debugging JS much harder - how are you going to find the returned code in your browser debugger? There may be some use cases for this type of design but they are probably very rare.
The better / more idomatic solution is to send JSON data (eg. the rendered text) back to the browser and have some handler parse the JSON and update the DOM. This way all of your JS code is present in the browser and all you are receiving is data. This will be much easier to debug.

Categories