Home > Mobile >  How to pass user's browser geolocation (from javascript) to rails?
How to pass user's browser geolocation (from javascript) to rails?

Time:01-30

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.

  • Related