I'm currently setting the user's coordinates (latitude and longitude) using request.location.coordinates
, but the location is not accurate enough. I want to use the user's browser geolocation instead.
How can I pass the latitude and longitude gotten via javascript into the rails user_coordinates
variable? I've read that you should use AJAX for this, but I am a beginner and don't know how AJAX works. In particular, I've seen code snippets in various places but I don't know what file to even put them in to try it.
Update: It says here that I should use turbo streams for Ajax in Rails 7 - can anyone help me understand how this works? https://www.reddit.com/r/rails/comments/vfmymj/how_can_i_implement_ajax_in_rails_7/
controllers/application_controller.rb
class ApplicationController < ActionController::Base
before_action :set_user_coordinates
def set_user_coordinates
if Rails.env.production?
@user_coordinates = request.location.coordinates
end
end
end
javascript/controllers/geolocation_controller.js
import { Controller } from '@hotwired/stimulus';
export default class extends Controller {
static targets = ['park'];
connect() {
window.navigator.geolocation.getCurrentPosition((position) => {
this.setUserCoordinates(position.coords);
this.setDistanceText();
})
}
setUserCoordinates(coordinates) {
this.element.dataset.latitude = coordinates.latitude;
this.element.dataset.longitude = coordinates.longitude;
}
getUserCoordinates() {
return {
latitude: this.element.dataset.latitude,
longitude: this.element.dataset.longitude,
};
}
setDistanceText() {
this.parkTargets.forEach((parkTarget) => {
let distanceFrom = getDistance(
this.getUserCoordinates(),
{ latitude: parkTarget.dataset.latitude,
longitude: parkTarget.dataset.longitude },
);
parkTarget.querySelector('[data-distance-away]').innerHTML =
`${Math.round(convertDistance(distanceFrom, 'km'))}`;
});
}
}
Usage context: I am using the location data to sort parks based on distance from the user. I'm using Ransack to sort by distance, and distance is set via the Geocoder near
method, which uses the user_coordinates
variable to calculate distances:
controllers/parks_controller.rb
class ParksController < ApplicationController
def index
@parks = @q.result(distinct: true).includes(:visited_users, :favorited_users).near(@user_coordinates, 100000000).paginate(page:params[:page], :per_page => 24)
end
end
CodePudding user response:
To pass the latitude and longitude from JavaScript to Rails, you can make an AJAX request to a Rails endpoint with the coordinates as data. Here's how you could modify the setUserCoordinates
method to make the AJAX request:
setUserCoordinates(coordinates) {
this.element.dataset.latitude = coordinates.latitude;
this.element.dataset.longitude = coordinates.longitude;
fetch('/set_coordinates', {
method: 'post',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
latitude: coordinates.latitude,
longitude: coordinates.longitude
})
});
}
In your Rails routes.rb
file, add a new endpoint for the AJAX request:
Rails.application.routes.draw do
post '/set_coordinates', to: 'geolocation#set_coordinates'
end
Then create a new controller in Rails to handle the AJAX request:
class GeolocationController < ApplicationController
def set_coordinates
session[:latitude] = params[:latitude]
session[:longitude] = params[:longitude]
head :ok
end
end
Finally, in your ApplicationController, modify the set_user_coordinates method to use the latitude and longitude from the session:
def set_user_coordinates
if Rails.env.production?
@user_coordinates = [session[:latitude], session[:longitude]]
end
end
With these changes, the user's latitude and longitude will be sent to the server via AJAX and stored in the session, which can then be used in your Rails controllers.
Regarding Turbo Streams, it is a new feature in Rails 7 that allows for real-time communication between the server and client. However, you do not need it for the above solution.