Rails passing variable to jBuilder

亡梦爱人 提交于 2020-03-25 19:04:05

问题


Fixed and solutions incorporated into the OP. A combination of comments by @max here and to my related posting Rails connecting to jBuilder. Edits are with // Was:

I'm trying to get values through a has_many :through relationship. Three main databases: people, locations (which has street address and other information), and the join table, years which links the person to a particular address on a specific date.

# models/person.rb
class Person < ApplicationRecord
  has_many :years, dependent: :destroy 
  has_many :locations, through: :years

# models/location.rb
class Location < ApplicationRecord
  has_many :years
  has_many :people, through: :years

# models/year.rb
class Year < ApplicationRecord
  belongs_to :location
  belongs_to :person

years links the person to a particular address on a specific date.

As I understand it the flow is that the show.html.erb calls javascript/packs/olPersonMap.js which then calls show_locations.json.jbuilder. How do I pass the person.id to jbuilder?

From views/people/show.html.erb (class is probably not needed now, refactoring needed).

// Was: <div id="map" class="map"></div>
 <div id="map" class="map" data-url="<%= person_locations_path(@person) %>"></div>  
<%= javascript_pack_tag 'olPersonMap' %>
<div>

From javascript/packs/olPersonMap.js.

// Near the top of the file
var urlJSON = $('#map').data('url') + '.json'
// and much further down in the code
new VectorImageLayer({
  title: 'Where lived and worked',
  imageRatio: 2,
  source: new VectorSource({
    // Was: url: '../people/show_locations',
    url: urlJSON
    format: new GeoJSON()
}),

From @max jBuilder was in app/views/people/show_locations.json.jbuilder, now app/views/people/locations/index.json.jbuilder. Leaving code out as it wasn't the main problem (although it did have few errors).

And this

# app/controllers/people/locations_controller.rb
module People
  class LocationsController < ApplicationController
    before_action :set_person

    def index
      respond_to do |f|
        f.json
      end
    end

    private
    def set_person
      @person = Person.eager_load(:locations)
                      .find(params[:person_id])
      @locations = @person.locations
    end
  end
end

and routes.rb

resources :people do
  resources :locations, only: [:index], module: :people
end

In summary, how do I pass person.id to the jBuilder? Or am I going at this all wrong?

Thank you @max. I need to work on understanding what the controller is doing. Lots of moving parts. https://secure-shore-68966.herokuapp.com/people/124 is the Leaflet version. Will push this OpenLayers version after some refactoring to remove all the trial stuff.


回答1:


you can simply pass id or complete URL of action (which I would prefer) using HTML data attribute.

show.html.erb

<div id="person-details" data-url="<%= @person.id %>"></div>

javascript/packs/olPeopleMap.js

new VectorImageLayer({
  title: 'Where lived and worked',
  imageRatio: 2,
  source: new VectorSource({
    url: `../people/show_locations/${$('#person-details').data('id')}`,
    format: new GeoJSON()
}),



回答2:


First setup a nested route that delivers the JSON you want to consume:

resources :people do
  resources :locations, only: :index, module: :people 
end  

module People
  class LocationsController
    # GET /people/1/locations.json
    def index
      @person = Person.includes(:locations).find(params[:person_id])
      @locations = @person.locations
    end
  end
end

Rename your view app/views/people/locations/index.json.jbuilder.

Then setup the map container in the view:

<div id="map" class="map" data-url="<%= person_locations_path(@person) %>">

</div>

If your doing it the right way there is no reason to go near the .js.erb nonsense that just makes a mess of everything since it makes your server get involved in the state of the client.

Don't try to put different script tags on different pages. Its not a good approach - especially if turbolinks is involved. Instead create handlers that augment different elements on the page if the are present (in this case the #map element) that go in your "static" /javascripts/packs directory.

This keeps the seperation of concerns in place - the server just delivers data that the client consumes and it lets your javascript be concatenated, minified, cached and delivered via a CDN instead of having Rails serve up interpolated .js.erb soup.

It also avoids turning your Rails app into a bunch of RPC style routes that just update the client.

In OpenLayers you should be able to get the element that the map is bound to through the target property of the map.

new VectorImageLayer({
  title: 'Where lived and worked',
  imageRatio: 2,
  source: new VectorSource({
    url: map.target.dataset.url,
    format: new GeoJSON()
}),



回答3:


The answer is incorporated into the OP. Thank you @max.

PS. I changed olPeopleMap.js to olPersonMap.js as it was more logical.




回答4:


Rails runs on Server, JavaSciprt is scripting language runs on browser(client). Basically, there is no way to pass data from Rails to Javascript directly. So you need to third store to pass data between Rails and Javascript like HTML data attribute, HTML hidden filed,...



来源:https://stackoverflow.com/questions/59548291/rails-passing-variable-to-jbuilder

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!