Search code examples
javascriptjqueryruby-on-railscontrollermodels

Can not reach to associated models from Jquery


I have associated models. Boat & Location Model. I am trying to reach to Boat Model from Jquery code but I could not do it. The thing; I return all the locations to Jquery as listing and it contains address, boat_id etc. So listing.address works, listing.boat_id works but listing.boat.year (for instance) does not work. It gives an error of; Boat is not defined because it is not rails code, right?. But I could not find a way to pass it. It should be smth like <%= listing.Boat.model %>.

I wonder that might because of the js function uses parse.HTML. Here is the function, which inserts given listings to the dom;

/*
        *Insert given listings to the dom
        */
        function insert_listing( index , listing ){
                //create html for each listing using listing template function (options), and attach hover event listener which highlights the 
                //related marker when the listing is hovered (BECAUSE OF HERE??)
                var html = $($.parseHTML(settings.listing_template( listing ))).wrap( settings.listing_wrapper ).parent().attr( 'data-mid' , 

                index ).addClass( settings.listing_class ).mouseenter( function(){

                    var mid = $(this).attr('data-mid');

                    $.each(markers , function( index , marker ){

                        if( Number(marker.get('mid')) === Number( mid) ){

                            marker.setIcon( settings.highlighted_icon );

                        }

                    })

                }).mouseleave(function(){

                    var mid = $(this).attr('data-mid');

                    $.each(markers , function(index , marker){

                        if(Number(marker.get('mid')) === Number( mid)){

                            marker.setIcon( settings.icon );

                        }

                    })

                });

                $(settings.listings_el).append( html );
        }

Here are the associations; Boat has_one :location Location belongs_to :boat

Here is the jquery from html.erb file

<script>

    (function ( $ ) {

      $('#map-canvas').mapSearch({
        request_uri: 'locations/show.json', 
        initialPosition: [ <%= @initlat %> , <%= @initlng %> ], #THIS WORKS 
        filters_form : '#filters',
        listing_template : function(listing){ 
                    return '<div class="listing">'
                      +     '<h3>'+listing.address + '</h3>'
                                  //<%= listing.Boat.model %> DOES NOT WORK
                                  //<%= listing.boat.model %> DOES NOT WORK 
                      +   '<div class="row">'
                      +        '<div class="col-sm-2">'
                      +         '<img class="thumbnail img-responsive" src="http://dummyimage.com/150x150/000/fff.jpg">'
                      +          '</div>'
                      +        '<div class="col-sm-5">'
                      +           '<p><strong>Address : </strong>' + listing.address+ '</p>'
                      +               '<p>'+listing.address+', '+listing.address+' '+listing.address+'</p>'
                      +               '<p>Reg Year: ' + listing.address+'</p>'
                      +          '</div>'
                      +        '<div class="col-sm-5">'
                      +         '<p><strong>Demo</strong> '+listing.address+'</p>'
                      +         '<p><strong>Demo</strong> '+listing.address+'</p>'
                      +          '</div>'
                      +   '</div>'
                      +  '</div>';
                  },
        marker_clusterer : true
      });
    }( jQuery ));

  </script>

I do not know if relevant this is the locations_controller, which sends data to jquery;

class LocationsController < ApplicationController

  def index

    if params[:search].present?   
      location = Geocoder.search(params[:search])
      @locations =location[0]

    else
        @locations = Location.all.first
    end
    @initlat = @locations.latitude
    @initlng = @locations.longitude

  end



  def show
#I ll process these later
    ne_lat = params[:ne_lat].to_f
    ne_lng = params[:ne_lng].to_f
    sw_lat = params[:sw_lat].to_f
    sw_lng = params[:sw_lng].to_f


 mylatlong2 = Location.all

    locs = {'results' => mylatlong2}
    respond_to do |format|
      format.html
      format.json {render json: locs}
    end
  end
end

So actually in the rails console, Location.last.Boat.year works for instance. Thank you for your help. I appreciate!.

PS: This is what I get from get request (controller);

{"results":[{"id":15,"address":"Ataköy Marina, 34140, Bakırköy, İstanbul, Türkiye","longitude":28.874432,"latitude":40.971388,"location_type":null,"boat_id":250,"created_at":"2015-05-17T11:36:29.245Z","updated_at":"2015-05-17T11:36:29.245Z","distance":0.060815068242947884,"bearing":135.0},{"id":16,"address":"Ataköy Marina, İstanbul, Türkiye","longitude":28.874432,"latitude":40.971388,"location_type":null,"boat_id":251,"created_at":"2015-05-21T21:27:43.366Z","updated_at":"2015-05-21T21:27:43.366Z","distance":0.060815068242947884,"bearing":135.0},{"id":1,"address":null,"longitude":28.87443200000007,"latitude":40.971388,"location_type":null,"boat_id":null,"created_at":"2015-05-12T08:01:08.899Z","updated_at":"2015-05-12T08:01:19.769Z","distance":0.06081506824595426,"bearing":135.0},{"id":2,"address":null,"longitude":28.87443200000007,"latitude":40.971388,"location_type":null,"boat_id":null,"created_at":"2015-05-12T08:01:26.659Z","updated_at":"2015-05-12T08:02:14.615Z","distance":0.06081506824595426,"bearing":135.0},{"id":3,"address":null,"longitude":28.87443200000007,"latitude":40.971388,"location_type":null,"boat_id":null,"created_at":"2015-05-12T08:02:32.089Z","updated_at":"2015-05-12T08:03:11.074Z","distance":0.06081506824595426,"bearing":135.0},{"id":4,"address":null,"longitude":28.87443200000007,"latitude":40.971388,"location_type":null,"boat_id":null,"created_at":"2015-05-12T08:03:14.706Z","updated_at":"2015-05-12T08:03:50.343Z","distance":0.06081506824595426,"bearing":135.0},{"id":5,"address":"Ataköy Marina, 34140, Bakırköy, İstanbul, Türkiye","longitude":28.87443200000007,"latitude":40.971388,"location_type":null,"boat_id":242,"created_at":"2015-05-12T08:03:56.194Z","updated_at":"2015-05-12T08:03:56.194Z","distance":0.06081506824595426,"bearing":135.0},{"id":6,"address":"Ataköy Marina, 34140, Bakırköy, İstanbul, Türkiye","longitude":28.87443200000007,"latitude":40.971388,"location_type":null,"boat_id":243,"created_at":"2015-05-12T08:07:03.799Z","updated_at":"2015-05-12T08:07:03.799Z","distance":0.06081506824595426,"bearing":135.0},{"id":7,"address":"Ataköy Marina, 34140, Bakırköy, İstanbul, Türkiye","longitude":28.87443200000007,"latitude":40.971388,"location_type":null,"boat_id":244,"created_at":"2015-05-12T08:08:25.252Z","updated_at":"2015-05-12T08:08:25.252Z","distance":0.06081506824595426,"bearing":135.0},{"id":8,"address":"Ataköy Marina, 34140, Bakırköy, İstanbul, Türkiye","longitude":28.87443200000007,"latitude":40.971388,"location_type":null,"boat_id":245,"created_at":"2015-05-12T08:20:38.188Z","updated_at":"2015-05-12T08:20:38.188Z","distance":0.06081506824595426,"bearing":135.0},{"id":9,"address":"Ataköy Marina, 34140, Bakırköy, İstanbul, Türkiye","longitude":28.87443200000007,"latitude":40.971388,"location_type":null,"boat_id":246,"created_at":"2015-05-12T08:21:34.724Z","updated_at":"2015-05-12T08:21:34.724Z","distance":0.06081506824595426,"bearing":135.0},{"id":10,"address":"Ataköy Marina, 34140, Bakırköy, İstanbul, Türkiye","longitude":28.87443200000007,"latitude":40.971388,"location_type":null,"boat_id":247,"created_at":"2015-05-12T08:22:49.400Z","updated_at":"2015-05-12T08:22:49.400Z","distance":0.06081506824595426,"bearing":135.0},{"id":11,"address":"Fenerbahçe Yat Limanı, Kadıköy, Türkiye","longitude":29.037873999999988,"latitude":40.970286,"location_type":null,"boat_id":248,"created_at":"2015-05-12T08:23:43.187Z","updated_at":"2015-05-12T08:23:43.187Z","distance":7.03002425919155,"bearing":135.0},{"id":13,"address":"Fenerbahçe Yat Limanı, Kadıköy, Türkiye","longitude":29.037874,"latitude":40.970286,"location_type":null,"boat_id":249,"created_at":"2015-05-12T14:59:01.117Z","updated_at":"2015-05-12T14:59:01.117Z","distance":7.030024259192001,"bearing":135.0},{"id":14,"address":"Fenerbahçe Yat Limanı, Kadıköy, Türkiye","longitude":29.037874,"latitude":40.970286,"location_type":null,"boat_id":null,"created_at":"2015-05-17T11:36:03.798Z","updated_at":"2015-05-17T11:36:05.476Z","distance":7.030024259192001,"bearing":135.0}]}

and the js code gets it as listing. So I thought; <%= listing.boat.model %> should work but gives error;

NameError in LocationsController#index
undefined local variable or method 'listing' for #<#<Class:0x007f9665a25758>:0x007f96658c93a0>

EDIT 1:

This is the full js file;

(function ( $ , window , undefined) {

    $.fn.mapSearch = function ( options ) {
            var el , settings , markers = [] , params , extra_params = [] , marker , markerClusterer, mapOptions , map , infowindow;

            el = this[0];

            /*
            *Default settings for the plugin.
            */
            settings = $.extend({

                /*
                *Number: Initial zoom level of the map.
                */
                zoom: 6,        

                /*
                *Array: Initial position coordinates of the map [latitude , longitude].
                */
                initialPosition: [40, -100],

                /*
                *String: URL where the plugin will make ajaz request for json data
                */
                request_uri : '',   

                /*
                *String: ID of the element you want to insert listings in
                */
                listings_el : '#ms-listings',

                /*
                *String: ID of the element you want to insert pagination in
                */
                pagination_el : '#ms-pagination',

                /*
                *Function: Template function that receives a listing as parameter and returns HTML for it
                */
                listing_template : function(listing){   
                                        return '<div class="listing">'
                                            +     '<h3>'+listing.name + '</h3>'
                                            +     '<p><strong>Address : </strong>' + listing.address+ '</p>'
                                            +     '<p><strong>Women owned:</strong> '+listing.women+'</p>'
                                            +     '<p><strong>Accept Govt Credit card:</strong> '+listing.gcc+'</p>'
                                            +  '</div>';
                                    },

                /*
                *String: Wrapper around each listing
                */
                listing_wrapper : '<div></div>',    

                /*
                *Function: Receives entire response as parameter and returns page number identifier in the response
                */
                page_number : function(data){   
                    return 3/*data.meta.page BURASI DEĞİŞECEK!!!!!!*/; 
                },

                /*
                *Function: Receives a listing as parameter and returns an array of latitude and longitude pair
                */
                listing_latlng: function(listing){
                    return [listing.latitude , listing.longitude];
                },

                /*
                *Function: Receives a listing as parameter and returns Content for infowindow of each marker
                */
                infowindow_content : function(listing){ 
                    return '<div>' + listing.name + '<div>';
                },

                /*
                *String : Class to be added to map container
                */
                map_class : 'ms-map',

                /*
                *String : Class to be added to listings container
                */
                listings_class : 'ms-results',

                /*
                *String : Class to be added to single listing container
                */
                listing_class : 'ms-listing',

                /*
                *String : Class to be added to pagination links
                */
                pagi_link_class : 'ms-pagination-button',

                /*
                *String : Text that appears on link to next page
                */
                next_btn_text : 'Next', 

                /*
                *String : Text that appears on link to previous page
                */
                prev_btn_text : 'Previous',

                /*
                *String : Class to be added to show loading indicator
                */
                loading_class : 'loading',  

                /*
                *String : Path to marker icon to be used for each listing
                */
                icon : "<%= asset_path 'normal.png' %>",    //Icon to be used on map

                /*
                *String : Path to highlighted marker icon to be used for each listing
                */
                highlighted_icon : "<%= asset_path 'highlight.png' %>", 

                /*
                *String : Identifier of the listings array in response data
                */
                results_key : 'results',

                /*
                *Boolean : False for no filters, form ID for using filters
                */
                filters_form : false,

                /*
                *Boolean : To enable marker clusters, set this to true, otherwise false
                */
                marker_clusterer : false,

                /*
                *Boolean : To enable search box, set this to true, otherwise false
                */
                search_box : true,

                /*
                *String : Class to be added to search box
                */
                searchbox_class : 'form-control',

                searchbox_placeholder : 'Search for a location',
            }, options );

            /*
            *Adding Plugin Classes
            */
            $( settings.listings_el ).addClass( settings.listings_class );

            $( el ).addClass( settings.map_class );

            /*
            *Initializing the map
            */
            mapOptions = {

              center: new google.maps.LatLng( settings.initialPosition[0] , settings.initialPosition[1] ),

              zoom: settings.zoom,

              streetViewControl: false,

              panControl: true,
              panControlOptions: {
                position: google.maps.ControlPosition.RIGHT_BOTTOM
              },
              zoomControlOptions: {
                    style: google.maps.ZoomControlStyle.LARGE,
                    position: google.maps.ControlPosition.LEFT_BOTTOM
              },

            };
            map = new google.maps.Map( el, mapOptions );


            /*
            *Add searchbox if enabled in options
            */
            if( settings.search_box ){
                if(typeof google.maps.places === "object"){
                    $( el ).prepend( '<input style="top:25px !important;" type="text" id="search-form">' );
                    var input = (document.getElementById('search-form'));
                    $('#search-form').addClass( settings.searchbox_class ).prop('placeholder' , settings.searchbox_placeholder);
                    map.controls[google.maps.ControlPosition.TOP_CENTER].push(input);
                    var searchBox = new google.maps.places.SearchBox(input);
                    google.maps.event.addListener(searchBox, 'places_changed', function() {
                        var place = searchBox.getPlaces();
                        if(place.length > 0){
                            map.panTo(place[0].geometry.location);
                        }
                    });
                }else{
                    alert('Google Places library not found')
                }
            }


            /*
            *Initializing the InfoWindow for markers
            */
            infowindow = new google.maps.InfoWindow();

            /*
            *Initializing clusterer if needed
            */
            if(settings.marker_clusterer && typeof MarkerClusterer === 'function'){
                markerClusterer = new MarkerClusterer(map, markers, {gridSize: 50, maxZoom: 15});
            }

            /*
            *Adding an event listener for every time the user moves or zooms the map, we refresh the listings search
            */
            new google.maps.event.addListener( map , 'idle' , function(){

                params = get_bounds();

                request_listings( extra_params );

            });

            /*
            *Utility function: To get the bounds of the map
            */
            function get_bounds(){

                return {

                    sw_lat : map.getBounds().getSouthWest().lat(),

                    sw_lng : map.getBounds().getSouthWest().lng(),

                    ne_lat : map.getBounds().getNorthEast().lat(),

                    ne_lng : map.getBounds().getNorthEast().lng()

                };

            }


            /*
            *Function to make an ajax request to server for data
            *Most of the other functions are called inside this one.
            */
            function request_listings( new_params ){

                //checking if the request url is provided
                if(settings.request_uri != ''){

                    //Add loading indicator class
                    $(el).addClass( settings.loading_class );
                    $(settings.listings_el).addClass( settings.loading_class );

                    //make the ajax request with all the parameters as get variables
                    $.get(settings.request_uri+ '?' + $.param( $.extend(params , new_params) ), function(data){

                        //deleting old markers
                        deleteMarkers();

                        //removing old listings
                        $(settings.listings_el).empty();

                        //processing the returned data. Adding listings to the page and create markers
                        $.each(data[ settings.results_key ] , function( index , value ){
                            insert_listing( index , value );
                            addMarker( index , value );
                        });

                        //remove the previous pagination and add new one
                        $(settings.pagination_el).empty().prepend(create_pagination(settings.page_number(data)));
                        //bind click events to pagination buttons
                        $('.'+settings.pagi_link_class).click(function(){

                            extra_params['page'] = $(this).data('href');

                            $.fn.mapSearch.update(extra_params);
                        });


                        //add markers to the map
                        if(settings.marker_clusterer && typeof MarkerClusterer === 'function'){
                            markerClusterer.clearMarkers();
                            markerClusterer.addMarkers(markers);
                        }else{
                            showMarkers();
                        }

                        //remove loading class
                        $(el).removeClass( settings.loading_class );
                        $(settings.listings_el).removeClass( settings.loading_class );

                    });
                }
            }
            //Handle the filters form.
            processFilters();


            /*
            *Attach the infowindow created previously to a marker that is clicked, receives a mcontent string and marker as parameter
            */
            function makeInfoWindowEvent( contentString, marker ) {

                google.maps.event.addListener( marker, 'click', function() {

                infowindow.setContent( contentString );

                infowindow.open( map, marker );

              });

            }

            /*
            *Creates a marker from given listing and adds to the markers array
            */
            function addMarker( index , listing ) {

              var latlng = settings.listing_latlng( listing );

              var marker = new google.maps.Marker({

                position: new google.maps.LatLng( latlng[0], latlng[1] ),

                title : listing.name,

                mid : index,

                icon : settings.icon,

                });

                if(!settings.marker_clusterer){
                    marker.setMap(map);
                }

                //Attach infowindow event to the marker
                makeInfoWindowEvent(settings.infowindow_content( listing ), marker );

                //add to array
                markers.push(marker);
            }



            //Set markers on the map
            function setAllMap(map) {

              for (var i = 0; i < markers.length; i++) {

                markers[i].setMap(map);

              }

            }

            //Remove markers from map
            function clearMarkers() {

              setAllMap(null);

            }

            //Show all markers present in array
            function showMarkers() {

              setAllMap(map);

            }

            /*
            *Function : Receives current page number as parameter, returns HTML for pagination
            */
            function create_pagination(page_number){

                var prev = page_number > 1 ? '<li><a href="#" class="'+ settings.pagi_link_class +'" data-href="'+ (page_number - 1) +'">'+ settings.prev_btn_text +'</a></li>' : '';

                var next = '<li><a href="#" class="'+ settings.pagi_link_class +'" data-href="'+ (page_number + 1) +'">' + settings.next_btn_text + '</a></li>';
                return $(prev + next).wrapAll('<ul></ul>').parent().addClass('pagination');

            }
            /*
            *Attach an event listener to filters form
            *Whenever form values are changed, update the map and listings
            */
            function processFilters(){

                if(settings.filters_form != false && typeof settings.filters_form === 'string'){

                    $(settings.filters_form + ' :input').change(function(){

                        var filters = $(settings.filters_form).serializeArray();

                        var params = [];

                        for(var i in filters){

                                params[filters[i].name] = filters[i].value;

                        }

                        $.fn.mapSearch.update(params);

                    });

                }

            }

            /*
            *Delete all markers from array
            */
            function deleteMarkers() {
              clearMarkers();
              markers = [];
            }


            /*
            *Insert given listings to the dom
            */
            function insert_listing( index , listing ){
                    //create html for each listing using listing template function (options), and attach hover event listener which highlights the 
                    //related marker when the listing is hovered
                    var html = $($.parseHTML(settings.listing_template( listing ))).wrap( settings.listing_wrapper ).parent().attr( 'data-mid' , 

                    index ).addClass( settings.listing_class ).mouseenter( function(){

                        var mid = $(this).attr('data-mid');

                        $.each(markers , function( index , marker ){

                            if( Number(marker.get('mid')) === Number( mid) ){

                                marker.setIcon( settings.highlighted_icon );

                            }

                        })

                    }).mouseleave(function(){

                        var mid = $(this).attr('data-mid');

                        $.each(markers , function(index , marker){

                            if(Number(marker.get('mid')) === Number( mid)){

                                marker.setIcon( settings.icon );

                            }

                        })

                    });

                    $(settings.listings_el).append( html );
            }

            /*
            *A utility method for updating the map with some new parameters
            */
            $.fn.mapSearch.update = function(new_params){

                                        extra_params = new_params;

                                        params = get_bounds();

                                        request_listings( extra_params );

                                        delete extra_params['page'];
                                }
    }

})( jQuery );

Solution

  • You are most likely trying to use javascript variable in ruby.

    This kind of confusion is one many reasons to avoid intermingling your client side and server side code.

    Instead you can use data attributes and ajax to pass data to javascript.

    <div id="map-canvas" data-initlat="<%= @initlat %>"></div>
    

    Or another example:

    <body data-assets-path="<%= assets_path %>">.
    

    In your case you need to make sure that whatever action request_listings calls delivers the JSON data needed to render the listing.