Ok, I know the answer is AJAX call, but I have a particular problem:
Everything happens at my homepage in 2 steps:
1- The main user access the root "/"
2- at my index page I have a JS that get his current location
(latitude & longitude) AND reloads the page with the information in
hands with NO user interaction - then showing the page correct.
The problem: I don't know how to make an ajax call doing this ( I don't want to reload all the layout doing window.location ), I want just the index.html.erb, got it? But it's too hard man, I'm on this like a month and can't do it :( Please, someone help me, I really need. Thanks a lot.
You don't need to touch the controller for that. In your erb file, something like this could work:
<script>
if (<%=params[:geo_location].blank? ? 'true' : 'false' %>) {
locationData = DO_YOUR_THING_HERE;
$.get('/', { geo_location: locationData});
}
</script>
This way, when the page first loads, an ajax request will be sent with the parameters.
After the page loads in the second time, the location data will be accessible from the erb file from the params[:geo_location] object.
If you are just willing to refresh a part of the page, you can do something like:
$('#container').load('/A_NEW_CONTROLLER_PATH', {geo_location: locationData});
and set up a A_NEW_CONTROLLER_PATH to return only the partial HTML your interested in. you can do that using render 'index', layout: false in your controller action.
When you retrieve the current location, you have a callback in Javascript, that tells you that everything is finished. Inside this callback, you have access to the position in Javascript, right?
Then you could just post an AJAX request to a route in your Rails app. For example:
routes.rb
post "/user_position" => "position#update"
position_controller.rb
class PositionController < ApplicationController
def update
puts params
# here store the params in your db, or do whatever you want
end
end
JS:
function successGeoCallback(data) {
$.post("/user_position", { lat: data.latitude, lng: data.longitude })
.done(function(data) {
alert("Position was stored" + data);
});
}
Related
I posted a very poor question about this earlier, so I am reposting and making it MVCE.
I'm building a messaging service with Rails and AJAX. So far I can submit a message through a form, it will update in the HTML DOM, an AJAX POST method will send it to the controller, and the controller will save it in the database.
Now I need to add an AJAX method that will GET the message that was just submitted -- so that other users (in other browsers) will be able to view it.
Currently, and this is a hack job way of doing it, in my JS code I set a timeout that calls an AJAX GET function every half second. Is there a better way to do this -- as in, once the controller saves the message can it call the AJAX function? The AJAX code looks like this:
function retrieveMessages(){
var message;
<%debugger%>
$.ajax({
type:"GET",
url:"<%= messages_get_path %>",
dataType:"json",
data: { what_goes_here: "blah" }, //this is the part I do not understand -- see below
success:function(data){
message = data;
console.log(data)
}
});
setTimeout(retrieveMessages, 500);
}
$(document).ready(function(){
//get messages
setTimeout(retrieveMessages, 500);
... more irrelevant
The line data: { what_goes_here: "blah" } doesn't make sense to me. What is the syntax for the controller to send data back to be stored into data:? Furthermore, from the console I can see that what_goes_here is being passed as a parameter to the controller -- again this doesn't make sense to me.
My route looks like this get 'messages/get', :to => 'messages#get' (this might be incorrect?)
rake routes shows
messages_get GET /messages/get(.:format) messages#get
And as of now, I don't have anything in my controller other than a respond_to because at this point I'm just trying to call the controller. What is the syntax to send data back to the AJAX method?
def get
debugger
respond_to do |format|
format.html
format.json {render json: #variable} //is this #variable being passed to the AJAX call?
end
end
UPDATE
This makes more sense to me... the AJAX method simply calls the def get function. The def get function then finds the message in the database, and stores it in an instance variable. Subsequently, I can add some Javascript code that will insert it into the DOM. However I must have something wrong in my routing because I'm getting (in the console) http://localhost:3000/messages/get 404 (Not Found)
What you are doing, as you suspect, is not effective. The more users are online the more will be load from these refreshing requests, most of them probably returning no new data.
You should consider more active way of notifying your browsers about changes on the server. One option is to use ActionCable.
Rails 3, JRuby
I recently took part in a quick crash course in jQuery that included a bit of ajax partial rendering. This got me thinking, could I use this to poll the Rails server using setInterval(), every x seconds to refresh a specific part of my page constantly?
The problem I'm having is how I could use the $.get() method to grab the url of the partial and reload it using load(). This is where the confusion starts- using Rails 3, I have a partial called "_microposts", rendered within a div with an 'id="gf" ' (gf meaning global feed). This happens on my Rails app homepage, so the url in this case would be "//localhost:8080/home" and not the url of the partial.
Here is my initial javascript/ jQuery
<script>
$(document).ready(function() {
setInterval(function (e) {
var url = $.get("<%= escape_javascript render :partial =>
'microposts/micropost', :locals => {:microposts => #microposts }%>");
$('#gf').html('loading...').load(url);
},10000);
});
</script>
This looks wrong, and so far, just blanks out my _microposts partial after 10 seconds (so the setInterval is working, and it's definitely updating the correct area, just with a blank space!)
Edit:
Thinking about my problem, I realised that this is similar to updating a partial from an event, such as clicking a button or something. The only real difference is the "event" that should trigger this the setInterval() function. So, my revised jQuery code is as follows:
<script>
$(document).ready(function() {
setInterval(function (e) {
$('#gf').html("<%= escape_javascript render :partial =>
'microposts/micropost', :locals => {:microposts => #microposts } %>")},
10000);
});
</script>
Unfortunately now, nothing seems to be happening from a user point of view, but the server is showing an ajax request every 10 seconds.
So why can't I poll for updates using ajax, and apply the changes to my _microposts partial? Is $.get the correct function to use in this case? What would the url for the load() method be when trying to re-load a partial?
Thanks,
Hopefully this will help anybody who wants to refresh a partial using ajax- especially if you're a beginner following Michael Hartl's tutorials to learn Ruby on Rails. Here's how I managed to solve my problem.
Firstly, I created a separate .js.erb file in the micropost view folder called 'polling.js.erb' that will refresh the global feed partial.
$('#gf').html("<%= escape_javascript render :partial =>
'microposts/micropost', :locals => {:microposts => #mps} %>");
I needed to write a method in the micropost controller that will correspond with the above javascript- this essentially supplies the information needed to refresh the partial. It's basically a simplified version of my index method in the micropost controller and avoids executing the additional code that's not needed for the partial I want to refresh.
def polling
#mps = Micropost.all #add some paginate code if you wish
#micropost = current_user.microposts.build(params[:micropost])
end
I then revised my javascript code, as I wanted to call the polling method every 5 seconds, loading the information specific to the current_user of my webapp.
$(document).ready(function() {
setInterval(function () {
$.ajax('microposts/<%= current_user.id %>/polling');
} , 5000);
});
Finally, I updated my routes.rb file to allow a web browser to call my polling method, using a get request, without causing a routing error. I'm using a member do block because the request is passing the current_user id via the request.
resources :microposts do
member do
post :polling
end
end
I currently have the following code to load a partial when I scroll to the bottom of a div with a table:
$(document).ready(function () {
$("#pastGigs").scroll(function () {
if (isScrollBottom()) {
$('#pastGigs tr:last').after('<%= render "layouts/pastGigs" %>');
$(this).unbind("scroll");
}
});
function isScrollBottom() {
var elementHeight = $("#pastGigs")[0].scrollHeight;
var scrollPosition = $("#pastGigs").height() + $("#pastGigs").scrollTop();
return (elementHeight == scrollPosition);
};
});
It works fine, but the reason I'm using the partial is because I don't want everything to load immediately, as it really slows the page down. Should I not be using a partial at all, or is there another way to execute the script and load the content only after scrolling? I've had a look around similar questions on stackoverflow, they all explain how to render the partial (I didn't know about escape_javascript and deleted all whitespace in the partial manually...), but none seem to solve my current speed issue. There is a difference of about 15 seconds (!) due to the amount of data in the partial, which is why I don't want to load it synchronously.
EDIT: Current config/routes.rb:
root "resnate_pages#home"
resources :users, :path => ''
EDIT 2: Current error in Terminal:
Processing by UsersController#show as */*
Parameters: {"id"=>"pastGigs"}
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1 [["id", "pastGigs"]]
Completed 404 Not Found in 3ms
ActiveRecord::RecordNotFound (Couldn't find User with id=pastGigs):
app/controllers/users_controller.rb:5:in `show'
After looking over everything, it looks like you have the right idea with rendering the partial except that, as we discussed, it is not actually being loaded asynchronously. Since you have the ERB tags in the body of the javascript, the ERB is actually being rendered server-side before being delivered to the browser (as you noticed, with the huge blob of HTML text). The reason this appeared to work (meaning avoiding the huge 15-second load times you mentioned) is because the HTML text isn't actually interpreted as HTML yet by the browser when the page loads (its just a plain 'ol string at this point). It will of course be parsed and evaluated when you do add it to the DOM with the .after function.
So basically we just need to ajax this stuff up. Good that you're looking at the jQuery ajax documentation; definitely worth the time and its pretty informative. There is also a $.get function which you might want to see as well. Its a convenience method for sending a GET request; you have less control, but if that doesn't matter, it can help keep things clean. I'll demonstrate with the $.get function, and you can choose to use the $.ajax function instead if you need the control (or prefer the other).
Put this in your scroll function:
$("#pastGigs").scroll(function () {
if (isScrollBottom()) {
$.get("/pastGigs", function(result){
$('#pastGigs tr:last').after(result);
});
$(this).unbind("scroll");
}
});
You can name the pastGigs route however you like, its only for demonstration purposes. Ensure you set up the route in your routes.rb
get "/pastGigs" => "users#pastGigs"
OR
get "/pastGigs", to: "users#pastGigs"
Again, I don't know what your controller is called, so replace it and the action name with the correct ones in your system.
And finally, the controller action:
def pastGigs
render :partial => "layouts/pastGigs"
# OR
# render "layouts/pastGigs", :layout => false
end
The end result will be that when the scroll reaches the bottom, an ajax call is fired off to that URL and your partial is rendered and returned to the success function (the second parameter of $.get). The result variable will contain the rendered HTML partial.
Let me know if that works.
Quickly adapted Paul's code to populate a modal for anyone who's interested:
$('#seeAll').click(function(){
$.get("/pastGigs", function(result){
$('#pastGigsBody').html(result);});
});
When the user clicks on "See All" in the Past Gigs div, it opens the modal with the all of the past gigs. Thanks to Paul for teaching me about the $.get request.
I'm working on an application in which a certain model is updated from a number of different locations using remote forms. I'm looking for a pattern to dynamically call the right JS callback after updating this model.
With a normal form this can be solved by passing a redirect url in the form itself and then redirecting to this url in the controller. The remote form side is harder: Should I pass a file name? Should I pass an arbitrary string and then switch on that string in a single .js.erb file? Any other suggestions?
Is this just a sign that the application should be restructured to prevent updating the same model from more than one location?
No it's fine If you can call the same controller action from different locations.
Your options:
1) Preferably this controller action can give the same response and will work for the different locations, ex. it just updates a container with a id which is present in all those locations.
2) You noted that redirects made things easy in the past, consider adding the following to your application controller:
def js_redirect_to(path, flash_messages = {})
flash_messages.each { |type, message| flash[type] = message }
respond_to do |format|
format.js { render :js => "window.top.location='#{path}';" }
end
end
This is the same signature as the normal redirect_to, it just allows you to redirect from a js request. Note that if you use turbolinks the js should be 'Turbolinks.visit(url);'.
3) If you really can't handle it generically like the options above, you could pass your JS namespace of the location you are submitting from in the form, and the controller calls the same method for all locations, it's just a different namespace. Ex:
Let say one location is from Pet administration, then in assets pet.js:
var pet = {
load = function() {
your page load js...
},
... more functions...
post_callback = function(html_segment1, html_segment2) {
this is where you handle the form callback for pets...
$('some_element').html(html_segment1);
$('another_element').html(html_segment2);
}
}
Construct more like these for other locations of your app. Using JS namespaces like this is anyway a good idea. Then your form submits a parameter :location => :pet to the controller, which responds with:
... your JS code that all pages should execute...
html_segment1 = "<%= escape_javascript(render 'some_partial') %>";
html_segment2 = "<%= escape_javascript(render 'other_partial') %>";
<%= #location %>.post_callback(html_segment1, html_segment2);
4) Use a widget gem, most popular is apotomo or cells.
5) Just use a case in the controller to render different views.
Hope this helps, let me know if you need clarification.
I am not sure if I worded my question correctly. I'm not actually sure how to go about this at all.
I have a site load.html. Here I can use a textbox to enter an ID, for example 123, and the page will display some information (retrieved via a Javascript function that calls AJAX from the Flask server).
I also have a site, account.html. Here it displays all the IDs associated with an account.
I want to make it so if you click the ID in account.html, it will go to load.html and show the information required.
Basically, after I press the link, I need to change the URL to load.html, then call the Javascript function to display the information associated with the ID.
My original thoughts were to use variable routes in Flask, like #app.route('/load/<int:id>') instead of simply #app.route('/load')
But all /load does is show load.html, not actually load the information. That is done in the Javascript function I talked about earlier.
I'm not sure how to go about doing this. Any ideas?
If I need to explain more, please let me know. Thanks!
To make this more clear, I can go to load.html and call the Javascript function from the web console and it works fine. I'm just not sure how to do this with variable routes in Flask (is that the right way?) since showing the information depends on some Javascript to parse the data returned by Flask.
Flask code loading load.html
#app.route('/load')
def load():
return render_template('load.html')
Flask code returning information
#app.route('/retrieve')
def retrieve():
return jsonify({
'in':in(),
'sb':sb(),
'td':td()
})
/retrieve just returns a data structure from the database that is then parsed by the Javascript and output into the HTML. Now that I think about it, I suppose the variable route has to be in retrieve? Right now I'm using AJAX to send an ID over, should I change that to /retrieve/<int:id>? But how exactly would I retrieve the information, from, example, /retrieve/5? In AJAX I can just have data under the success method, but not for a simple web address.
Suppose if you are passing the data into retrieve from the browser url as
www.example.com/retrieve?Data=5
you can get the data value like
dataValue = request.args.get('Data')
You can specify param in url like /retrieve/<page>
It can use several ways in flask.
One way is
#app.route('/retrieve/', defaults={'page': 0})
#app.route('/retrieve/<page>')
def retrieve():
if page:
#Do page stuff here
return jsonify({
'in':in(),
'sb':sb(),
'td':td()})
Another way is
#app.route('/retrieve/<page>')
def retrieve(page=0):
if page:
#Do your page stuff hear
return jsonify({
'in':in(),
'sb':sb(),
'td':td()
})
Note: You can specify converter also like <int:page>