I'm wondering how to get a dropdown that switches the page's language to work. I currently have the following:
<%= select('locale', 'id', %w(English Español Italiano 日本語 한국어), :onchange => "this.form.submit()") %>
Any idea how I can get this to switch the page's locale?
I am currently passing the locale via the params hash.
There are several problems:
You're trying to submit a select box..... but where does it submit to?
How are you managing the languages on your page?
--
When submitting with JS, you have to remember that it needs to somewhere to submit to.
Like a <form>'s action attribute, your JS needs a location to send the submit request. This is normally handled with a form, but can also be manually inputted with Ajax.
You may wish to use this:
#app/assets/javascripts/application.js
$(document).on("change", "select", function(e) {
$.ajax({
url: "your/url",
data: $(this).value(),
success: function(data) { ... },
error: function(data) { ... }
});
});
This will take the inputted value of the select box and send the data to a preformatted route in your Rails app. This leads nicely onto my second question -- how you get the language to remain in the app...
-
Regardless of your structure, you'll need to give your Rails app a way to determine the language you have selected.
If only to set a session variable, you have to pass your data through to your Rails controller.
To do this, you first need to ensure you have the routes and then the controller action required to handle the request:
#config/routes.rb
post :language, to: "application#language"
#app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
def language
session[:language] = params[:language] #-> this needs to be refactored
end
end
You'll then be able to pass back some response or command to invoke the language change on the page.
Although setting the language in the backend, as described above, may seem tedious, doing so will give you the ability to set the entire language parameter for the site -- IE if you wanted to use it for currency etc, it will be available in all the actions you need.
Hope this helps.
Related
I have a table of content with multiple rows.
For any given row I want to be able to verify a record and have that be reflected immediately on the screen by changing the "verify link" hyperlink to be just plain text 'verified'.
Seems like a fairly common pattern for others.
I have the following markup:
...# (table stuff).
= link_to 'verify', verify_link_path(:id => link.id), class: 'verify', remote: true
This (successfully) calls the links controller 'verify_link' method:
def verify_link
#link = Link.find(params[:id])
# Ignore additional verify logic for now, just set as verified (when the user clicks verify).
#link.verified_date = Time.now
#link.save!
respond_to do |format|
format.js
end
end
I know this works... because the database gets updated.
Finally I want to update the page (table-row-cell) to reflect this.
This is where I am stuck.
The controller method successfully calls the following app/views/links/verify_links.js.erb file:
$(function() {
$(".verify")
.bind("ajax:success",function(event,data,status,xhr) {
this.text='verified';
alert("The link was verified.");
});
});
I know it gets invoked... because I can put alert("hello world"); at the start and it gets displayed as a pop-up.
The part that isn't working is replacing the 'verify' link with plain text 'verified'.
Actually I can't even the alert to work in there at all.
Nothing gets updated in the ui. On the backend the change in the db is made however, so if I refresh the page I do see the change reflected at that point).
Notes:
Is there an issue with the fact that there are multiple links? i.e. Should I be using an id, which I would have to munge from the table row id or number
app version is rails 3.2.17 with the asset pipeline.
app was originally written in rails 2 and was later upgraded.
I am using jquery 1.7.2 and rails.js but avoiding frameworks like angular, ember, etc. initially as I want to learn more of the lower level stuff first. Once I do I will then use a framework.
#hjing is right. The code inside app/views/links/verify_links.js.erb is already a response to your ajax. You need not bind it to the ajax:success.
As you suggested you need to make an id for each link which you'll have to munge from the table row id or number. For example for your your links will look like this:
= link_to 'verify', verify_link_path(:id => link.id), class: 'verify', id: "verify_#{table_row_no}", remote: true
then inside your app/views/links/verify_links.js.erb you can target that particular link like this:
$("#<%= j 'verify_#{table_row_no}' %>").text("verified");
$("#<%= j 'verify_#{table_row_no}' %>").prop("href","#"); #you would also want to change the href in case someone clicks it again
I'm new to rails and still learning the ropes via railstutorial, but the book does all changes to the db via form submissions (http://api.rubyonrails.org/classes/ActionView/Helpers/FormHelper.html). But how would I go about submitting (updating the db) without this, lets say I want to add some score which is computed on page ,(for example via a js counter - for simplicity lets just say its a constant 10) and my db consists of a column called score. Then after pressing a submit button how would I go about updating the db?
Thanks
Two trivial ways:
Use a form
Use a URL parameter with a link
The processing (other than GET v. POST) on the Rails side is identical--it's just a parameter.
If you're using JavaScript, there's not necessarily a reason to not use a form, though, since you could just update a form input element and submit normally.
It is quite simple, actually.
The constant 10 is submitted from the view. The submit needs to point to the controller action that will handle this request. So the submit button should build the url using :controller, :action, :id parameters. In a form, this is handled in the form_for declaration. You will deal with in the button_tag declaration.
The routes should be configured so that this message can reach the controller/ action.
The constant 10 is transported in the params hash. If the field is my_counter, then look for params[:my_counter]. If the form had been for a model, say tweets, then it might be in params[:tweet][:my_counter].
In the controller action, possibly update, you will first fetch the record to change with something like #score = Score.find(:params[:id]). This params[:id] is also coming from the view with the submit. Change the counter here, and save.
def update
#score = Score.find(:params[:id])
#score.counter = params[:my_counter]
#score.save
redirect_to :action => :index # or wherever
end
Good luck.
I am in the process of building a facebook app that works through iFrame with Ruby On Rails.
My App does serve multiple clients, web, mobile, and facebook. And depending the type of client the UI renders different kind of views.
When the user connects to my app using the facebook page tab, I do get enough information (in params collection) to identify the user came from facebook. Based on that I can customize the views to fit into the iFrame.
But for subsequent requests, because they happens through iframe, there is nothing that tells this is a facebook request (as far as I can tell unless there is something in the headers which I dont know of).
I tried setting a cookie during the first request and that worked great. But the problem is when the user requested my app directly from another browser tab (not through facebook) the cookie was still present and the user ended up seeing the facebook(ised) UI, instead of Normal UI.
Anyone has a solution to this?
I recommend using a different route for Facebook tabs. So if your regular URLs look like this:
/foobar
Then you may want to use something like this for Facebook, by adding the "/fb" prefix (or similar) to your Facebook app's tab or canvas URL:
/fb/foobar
In your routes.rb, you can then pass a parameter to indicate that the user is viewing the page on Facebook, such as "channel=facebook" or "facebook=true".
Your routes.rb might look something like this:
scope 'fb', :channel => 'facebook' do
# ... your routes ...
end
scope :channel => 'web' do
# ... your routes ...
end
With these routes, each request originating from Facebook will automatically have a "channel=facebook" parameter. Of course, you'd still be responsible for making sure to generate the appropriate URLs within your app. You could add :as => 'facebook' and :as => web to the scopes above and use this to generate URLs using dedicated helpers (e.g. facebook_foobar_url, web_foobar_url). But the best way to do this depends a lot on the complexity of your app.
Alternatively, you could also use default_url_options in your controller to add a "channel=facebook" or "facebook=true" parameter to every generated URL if you detect that the current request originated from Facebook (either from the existence of a signed_request or from channel/facebook param you added). Note that this method is deprecated in Rails 3, and I'm not quite sure what (if any) the official replacement is in Rails 3.1 or 3.2.
I needed to solve this with the least amount of intrusion into the existing code.
And am posting my solution here for the benefit of anyone looking to solve similar problem.
I have a unique layout for facebook and this was invoked during the first request (like I mentioned above, the initial request posted from facebook tab has facebook params).
To make sure there was facebook param for subsequent requests, inside the facebook layout, I bound all the form submission and anchor click events to a method that would add a hidden form element and query string param respectively.
this is how the client side code looks like:
<script type="text/javascript">
$(document).ready(function() {
$('form').submit(function(e) {
$('<input>').attr({
type: 'hidden',
id: 'fb_param',
name: 'fb_param',
value: 'true'
}).appendTo($(this));
return true;
});
$('a').click(function(e) {
var newUrl = $(this).attr("href");
if (newUrl.indexOf("?") != -1) {
newUrl = newUrl + '&fb_param=true';
} else {
newUrl = newUrl + '?fb_param=true';
}
$(this).attr("href", newUrl);
return true;
});
});
</script>
Now to handle the server side redirects (typical when you update a resource etc), needed to extend the redirect_to method like so:
/app/conntrollers/application_controller.rb
class ApplicationController < ActionController::Base
def redirect_to(options = {}, response_status = {})
if params[:fb_param] == 'true'
if options
if options.is_a?(Hash)
options["fb_param"] = "true"
else
if options.include? "?"
options = "#{options}&fb_param=true"
else
options = "#{options}?fb_param=true"
end
end
end
end
super
end
end
I am currently using a link_to helper in View to pass parameters like title , author ,image_url and isbn back to controller
<%= link_to 'Sell this item',new_item_path(:title => title, :author => authors, :image_url=>image, :image_url_s=>image_s, :isbn=>isbn, :isbn13=>isbn13 ) %>
Controller will then assign the parameters to an object to be used by a form in View later(in new.html.erb)
def new
#item = Item.new
#item.title = params[:title]
#item.author = params[:author]
#item.image_url = params[:image_url]
#item.image_url_s = params[:image_url_s]
#item.isbn = params[:isbn]
#item.isbn13 = params[:isbn13]
respond_to do |format|
format.html # new.html.erb
format.xml { render :xml => #item }
end
end
new.html.erb will then be called.
This is all working fine but the url shows all the parameters
http://localhost:3000/items/new?author=Michael+Harvey&image_url=http://ecx.images-amazon.com/images/I/51vt1uVjvLL._SL160_.jpg&image_url_s=http://ecx.images-amazon.com/images/I/51vt1uVjvLL._SL75_.jpg&isbn13=9780307272508&isbn=0307272508&title=The+Third+Rail
Is there any way I can make the parameters not show up on the URL?
Maybe you could encode the parameters and decode them in the controller to deter users who may want to modify the url? Might be overkill but...
>> author=ActiveSupport::Base64.encode64("author=jim")
=> "YXV0aG9yPWppbQ==\n"
>> ActiveSupport::Base64.decode64(author)
=> "author=jim"
A POST can be used to move the parameters out of the URL and into the request, but this is not the "correct" or best practice. HTTP standards are such that non-GET requests are meant to be used only for requests that change state on the server. This is why you get a warning when you refresh a page that was generated in response to a POST.
There is nothing wrong with having parameters in the URL. So much focus should not be made on what appears to the URL bar, let alone what's after the ?. If however you have some need (i.e. insistence of a client) to remove them, you have several options, two of which John mentions.
I'm assuming your "new" action is REST-style, in that it's generating a form that would have to be submitted to change state on the server. Therefore your options might be:
Use POST, even though it's not standard compliant. Not recommended.
Use AJAX GET. This requires javascript, and ajax handling does add requirements such as the use of a JS framework and testing.
Use GET (or POST), but capture the parameters and store them, the redirect the user back to another clean URL that displays those stored value. You could store those in the session hash, or create a database record of them. Actually you really should use POST in this case, since you are effectively changing state on the server by storing those parameters. In this case, if the user refreshes the page he is directed to, those parameters will be preserved. This effectively removes the browser warning on refresh, something I can certainly appreciate.
There are two options that I can see and both involve JavaScript:
Have the link populate hidden form fields for the parameters and then submit the form using an HTTP POST request
Have the link submit an AJAX request to the controller action (using an HTTP GET unless clicking the link changes server-side state, in which case a POST should be used)
I think I would go with the second approach.
Why not write them to the session? It looks like you might have less than 4k in data there. Just remember to wipe it.
I have a search resource, the user can perform searches by filling out a form and submitting it, the create action is called, the Search is saved, the show action is called, and the results are displayed. This all happens with the default POST, and all works fine.
The user may want to save his search in the saved_search table (i don't use the Search table for this purpose as this table stores all searches for the purpose of compiling statistics, and gets cleared on a regular basis). Once the Search is saved, it can be re-run by clicking a hyperlink, this is where i start to get problems.
I see no way of getting my hyperlink to run the create action of Search, with a POST request, and the necessary data.
I then decided to try to get both form submission and the hyperlink to perform a search using a GET request, i was unable to get form_for to run my Search create action using a GET request, it always seems to get routed to my index action.
Can someone suggest a good restful solution to this problem please.
Many thanks
I'm not quite sure what you're trying to do here. If you want to have a form submit with a GET request, you can override the HTML attribute on the form_for helper:
<% form_for blarg, :html => { :method => 'get' } %>
blabla
<% end %>
Rails also supports a way of "faking" the HTTP method by using a "magic" parameter (called "_method"), which makes Rails behave as if it had seen the HTTP method in that parameter.
If you send the form as "get", you must make sure that none such parameter is set. If you wanted to let a hyperlink send a "POST", tweaking this would be the way (a browser will not send a real POST on a click on a link)
Jon,
If I understood right, if the search is already saved, you could just make a get on the resource of the saved search like you did the first time and use the show action to display the result.
Anyway, if you still wants to do a post with a link, the helper method link_to does it for you. Check it out:
http://www.51773.com/tools/api.rubyonrails.org/classes/ActionView/Helpers/UrlHelper.html#M001597
With a :method => :post option it will create a hidden form and post your data.
Hope it helps.