1

I am new to Ruby on Rails and am playing around with it before I start my bootcamp soon. I am making a app which takes a latitude and longitude position and adds it to a leaflet map embedded on the page. I have created a database which has two values being latitude and longitude, which updates when the user adds their respective values.

The issue which I am having is I am unable to figure out how to retrieve these two values from the database to add to the javascript in the script tag as the latitude and longitude values (L.marker([**tasks.latitude**, **tasks.longitude**]).addTo(mymap);). I am aware I can retrieve the values inside an erb tag like <% @tasks.each do |task| %> then <%= task.latitude %>, but you cannot use a erb tag inside the script tag (or .js file which I will convert it to once I figure it out). Would I have to use AJAX or something else to grab the values? I am just a bit lost and hoping to be pointed in the right direction.

Below is the code from my index.html.erb and from the database in rails console

`<head>
<%= javascript_pack_tag 'application' %>
<%= stylesheet_pack_tag 'index' %>

<link rel="stylesheet" href="https://unpkg.com/[email protected]/dist/leaflet.css" />
<p id="notice"><%= notice %></p>

<header class='header'>
    Parks Tasks
</header>

<main class="main">

    <%= link_to 'New Task', new_task_path %>

    <table class="table">
        <thead>
            <tr>
                <th>Latitude</th>
                <th>Longitude</th>
                <th colspan="3"></th>
            </tr>
        </thead>

        <tbody>
            <% @tasks.each do |task| %>.
            <tr>
                <td><%= task.latitude %></td>
                <td><%= task.longitude %></td>
                <td><%= link_to 'Show', task %></td>
                <td><%= link_to 'Edit', edit_task_path(task) %></td>
                <td><%= link_to 'Destroy', task, method: :delete, data: { confirm: 'Are you sure?' } %></td>
            </tr>
            <% end %>
        </tbody>
    </table>

</main>
<aside class='mapid' id="mapid">
    <script>
        var mymap = L.map('mapid').setView([-38.35909, 144.937757], 13);
        L.tileLayer(
            'https://api.mapbox.com/styles/v1/{id}/tiles/{z}/{x}/{y}?access_token={accessToken}', {
                attribution: 'Map data &copy; <a href="https://www.openstreetmap.org/">OpenStreetMap</a> contributors, <a href="https://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>, Imagery © <a href="https://www.mapbox.com/">Mapbox</a>',
                maxZoom: 18,
                id: 'mapbox/streets-v11',
                tileSize: 512,
                zoomOffset: -1,
                accessToken: 'removed for post',
            }
        ).addTo(mymap);
        L.marker([***tasks.latitude***, ***tasks.longitude***]).addTo(mymap);
    </script>
</aside>

<footer class="footer">Footer</footer>
`

=> #<ActiveRecord::Relation [#<Task id: 1, latitude: -0.3836327e2, longitude: 0.144877609e3, created_at: "2020-07-18 09:21:05", updated_at: "2020-07-18 09:21:05">, #<Task id: 2, latitude: -0.3836327e2, longitude: 0.144877609e3, created_at: "2020-07-18 09:26:41", updated_at: "2020-07-18 09:26:41">]> irb(main):006:0>

I would appreciate any help on the matter and if you need any more clarification, please ask.

Regards, Rory.

11
  • just update L.marker([tasks.latitude, tasks.longitude]).addTo(mymap) with L.marker([ <%= tasks.latitude %>, <%= tasks.longitude %>]).addTo(mymap); Commented Jul 18, 2020 at 12:24
  • here your file is index.html.erb so will work but as your question if it's a .js file then you can add .erb at the end of the file like (*.js.erb) and write ruby code just like in any .erb file, you would do Commented Jul 18, 2020 at 12:30
  • thanks for replying Jitendar, I have tried implementing that change before, but it doesn't recognize the ruby, which i think is because it is inside the script tag? It just then throws a syntax error and doesn't run. Any idea's how to fix that? Thanks again Commented Jul 18, 2020 at 12:42
  • what's the error? and file name Commented Jul 18, 2020 at 12:44
  • The error I'm getting is "Uncaught SyntaxError: expected expression, got '<' ", the file name I am working in is index.html.erb. VScode doesn't highlight the ruby code inside the script like it does outside of it, which says to me it doesn't accept it. Commented Jul 18, 2020 at 12:47

1 Answer 1

2
<script>
    var mymap = L.map('mapid').setView([-38.35909, 144.937757], 13);
    L.tileLayer(
        'https://api.mapbox.com/styles/v1/{id}/tiles/{z}/{x}/{y}?access_token={accessToken}', {
            attribution: 'Map data &copy; <a href="https://www.openstreetmap.org/">OpenStreetMap</a> contributors, <a href="https://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>, Imagery © <a href="https://www.mapbox.com/">Mapbox</a>',
            maxZoom: 18,
            id: 'mapbox/streets-v11',
            tileSize: 512,
            zoomOffset: -1,
            accessToken: 'removed for post',
        }
    ).addTo(mymap);
    // Iterate through the records in ERB and output a line for each Task
    <% @tasks.each do |t| %> 
      L.marker(<%= raw [t.latitude, t.longitude].to_json %>).addTo(mymap);
    <% end %>
</script>

But a better way to solve this without using an inline script tag and erb interpolation is to add data attributes to the map container and the items in the table.

<div id="map" data-lat="-38.35909" data-lon="144.937757" data-zoom="13">
</div>

<table class="table">
    <thead>
        <tr>
            <th>Latitude</th>
            <th>Longitude</th>
            <th colspan="3"></th>
        </tr>
    </thead>

    <tbody>
        <% @tasks.each do |task| %>
        <%= content_tag :tr, class: 'task' data: { lat: task.latitude, lon: task.longitude } do %>
            <td><%= task.latitude %></td>
            <td><%= task.longitude %></td>
            <td><%= link_to 'Show', task %></td>
            <td><%= link_to 'Edit', edit_task_path(task) %></td>
            <td><%= link_to 'Destroy', task, method: :delete, data: { confirm: 'Are you sure?' } %></td>
        <% end %>
    </tbody>
</table>

Data attributes give you a convenient way to pass data via the DOM to your javascript.

This lets you write better javascript that is placed in the assets pipeline and will be minified and concenated.

function initializeMap(element, markers){
  let data = element.dataset;
  let map = L.map(element)
             .setView([data.lat, data.lon], data.zoom || 13);
  L.tileLayer(
    'https://api.mapbox.com/styles/v1/{id}/tiles/{z}/{x}/{y}?access_token={accessToken}', 
    {
      attribution: 'Map data &copy; <a href="https://www.openstreetmap.org/">OpenStreetMap</a> contributors, <a href="https://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>, Imagery © <a href="https://www.mapbox.com/">Mapbox</a>',
      maxZoom: 18,
      id: 'mapbox/streets-v11',
      tileSize: 512,
      zoomOffset: -1,
      accessToken: 'removed for post',
    }
  ).addTo(map);
  for (let i = 0; i < markers.length; i++) {
    let t = markers.item(i).dataset;
    L.marker([t.lat, t.lon]).addTo(map);
  }
}
// Replace this with DOMContentLoaded if you are not using turbolinks
document.addEventListener("turbolinks:load", function() {
  let map_el = document.getElementById("map");
  if (map_el.length){
    initializeMap(map_el, document.getElementsByClassName("task"));
  }
});
Sign up to request clarification or add additional context in comments.

3 Comments

Yet another way to do this is to add a JSON response to the controller and send an AJAX request to load the data for the markers. This lets you do stuff like dynamically searching on the map or lazilily populating the map with markers as the user pans around.
Hi Max, thanks for answering with such a detailed post! I have spent the last few hours trying to implement it and am getting closer to it working, but the problem I am having is the leaflet-src.js code is not recognizing the el because it is stored in a nodelist, I am aware that I could loop through each nodeList and iterate so that the el.className is accessible, but I am wondering if there is an easier way to loop only once or to pass a value of el in before it goes to the leaflet-src.js code? Here is a screen shot of the debugger when it throws the error. ibb.co/kxbD6Fw Thanks!
After a few small tweaks (edited in code above) - this issue is now resolved. Thanks again to Max for providing the solution, it was extremely helpful.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.