Mateusz Urbański
Mateusz Urbański

Reputation: 7862

Ruby on Rails searching through ajax

In my application I have search that find proper cars. It works properly. But now I want to use ajax. My search is based on form_object:

class SearchForm
  include Virtus.model
  include ActiveModel::Naming
  include ActiveModel::Validations
  include ActiveModel::Conversion
  include ActiveModel::Model

  def persisted?
    false
  end

  attribute :car_class, String, default: "A"
  attribute :handover_location, Integer
  attribute :return_location, Integer
  attribute :handover_date, DateTime,
    default: ->(form, attribute) { Time.current }
  attribute :return_date, DateTime,
    default: ->(form, attribute) { form.handover_date + 7.days }
  attribute :car_body_style, Integer
  attribute :car_fuel, Integer
  attribute :car_seats, Integer
  attribute :car_transmission, Integer
  attribute :car_brand, Integer

  def submit
    Car.search({
      handover_date: handover_date,
      return_date: return_date,
      handover_location: handover_location,
      return_location: return_location,
      car_class_id: car_class,
      car_body_style: car_body_style,
      car_fuel: car_fuel,
      car_seats: car_seats,
      car_transmission: car_transmission,
      car_brand: car_brand
    })
  end
end

I have controller "Cars" with action index which is responsible for searching proper cars:

class CarsController < ApplicationController
  before_action :attributes_from_params
  skip_authorization_check

  def index
    @search_form = SearchForm.new(params[:search_form])
    @cars = Car.search(@search_form)
     respond_to do |format|
      format.html
      format.js
    end
  end

  private

  def attributes_from_params
    @car_class = CarClass.where(id: params[:search_form][:car_class]).first.name
    @handover_location = "#{Location.where(id: params[:search_form][:handover_location]).first.name } 
      #{params[:search_form][:handover_date]}"
    @return_location = "#{Location.where(id: params[:search_form][:return_location]).first.name } 
      #{params[:search_form][:return_date]}"
    @handover_date = params[:search_form][:handover_date].to_date
    @return_date = params[:search_form][:return_date].to_date
    @days = @return_date - @handover_date
  end
end

My index views looks like this:

 #reservation.row
  = horizontal_simple_form_for @search_form, {url: cars_path, method: :get, remote: true} do |f|
    .basic_search
      = f.input :handover_location, label: I18n.t('.handover'), collection: Location.all.map{|hl| [hl.name, hl.id]}
      = f.input :handover_date, as: :string, label: false
      = f.input :return_location, label: I18n.t('.return') ,collection: Location.all.map{|rl| [rl.name, rl.id]}
      = f.input :return_date, as: :string, label: false
      = f.input :car_class, label: I18n.t('.car_class') ,collection: CarClass.all.map { |c| [c.name, c.id] }
    #search-results.row
      #search-filters.col-md-3
        %h4.filter-header
          = t('search.car_type')
        .filter-content.car-type
          = f.input :car_body_style, as: :check_boxes, collection: CarBodyStyle.all.map{|x| ["#{x.name}", "#{x.id}"]}, label: false
        %h4.filter-header
          = t('search.seats')
        .filter-content.number-of-people
          .checkbox
            = f.input :car_seats, as: :check_boxes, collection: [["2", "2"], ["3-5", "5"], ["6+", "6"]], label: false
        %h4.filter-header
          = t('search.fuel')
        .filter-content
          = f.input :car_fuel, as: :check_boxes, collection: [["#{t('enumerize.car.fuel.petrol')}", "petrol"], ["#{t('enumerize.car.fuel.petrol_lpg')}", "petrol_lpg"], ["#{t('enumerize.car.fuel.diesel')}", "diesel"]], label: false
        %h4.filter-header
          = t('search.transmission')
        .filter-content
          = f.input :car_transmission, as: :check_boxes, collection: [["#{t('enumerize.car.transmission.manual')}", "manual"], ["#{t('enumerize.car.transmission.automatic')}", "automatic"], ["#{t('enumerize.car.transmission.semiautomatic')}", "semiautomatic"]], label: false
      #search-cars.col-md-9
        = render @cars

        %br/
      = f.submit t('home.search'), class: "btn btn-default pull-right" 

and "_car" partial:

.single-car
  .row
    .car-img.col-sm-2.col-md-3.col-lg-4
      = image_tag image_path("portal/car-sample.jpg"), width: "140", height: "26"
    .car-info.col-sm-7.col-md-7.col-lg-6
      %h2= car.car_model
      %p.location
        = car.rental_company.name
        %br/
        = car.rental_company.correspondence_address.to_label if car.rental_company.correspondence_address
      .details
        %span.passengers
          = car.seats
        %span.doors
          = car.doors
        %span.air-conditioning
          = car_air_condition(car)
        %span.gearbox
          = car.transmission.try(:text)
        %span.fuel
          = car_fuel(car)
      .other-details
        = t('search.regulations')
        |
        = t('search.distance_limit')
        BRAK DANYCH
        = image_tag image_path("portal/mastercard.jpg"), width: "26", height: "15"
        = image_tag image_path("portal/visa.jpg"), width: "32", height: "10"
    .car-price.col-sm-3.col-md-2.col-lg-2
      %p.overall-price
        = number_to_currency(car.price_for_day(@handover_date, @return_date) * @days)
      %p.daily-price
        = t('search.for_day')
        = number_to_currency(car.price_for_day(@handover_date, @return_date))
      %p.deposit-price
        = t('search.caution')
        Brak Danych
        = hidden_field :test, "as"
        = link_to t('search.reservate'), new_reservation_path(search_form: params[:search_form], car: car.id), class: 'btn'
  %hr/ 

I've also created an index.js.erb file which looks like this:

$("#search-cars").html("<%= escape_javascript(render(@cars)) %>");

This simple ajax search works properly but I must click chceckbox and then click submit button. I want to have checkboxes which are also submit buttons. Is there any way to do this thing? I don't have to much expierience with ajax so I will be very greatful for any help.

Upvotes: 2

Views: 6079

Answers (3)

Richard Peck
Richard Peck

Reputation: 76774

We've done Ajax search before (you can check here)

The way to do it is quite simple. I know you've fixed it, but I guess having more examples will give you some really nice ideas:

--

Ajax

Ajax is very simple when you understand it

It basically makes an asynchronous request to your server through javascript, allowing you to render the response on the page. The way it works is to add to the http protocol (which basically just responds to single requests made to the server). Ajax essentially gives you the ability to create a "no refresh" experience


Example

enter image description here

Routes

#config/routes.rb
match 'search(/:search)', :to => 'products#search', :as => :search, via: [:get, :post]

--

View (we use form_tag - not form object)

<%= form_tag search_path, :method => :post, :id => "SearchForm" do %>
    <%= text_field_tag :search, params[:search], placeholder: 'Search your favourite products or brands', :autocomplete => :off, :id => 'SearchSearch'  %> 
    <%= image_submit_tag('nav_bar/search.png', title: 'Search', class: 'search_submit', data: { "placement" => "bottom" }) %>
<% end %>       

--

Controller

#app/controllers/products_controller.rb
def search      
    @products = Product.search(params[:search])
    respond_to do |format|
     format.js  { render :partial => "elements/livesearch", :locals => {:search => @products, :query => params[:search]} }
     format.html    { render :index }
    end
end

--

JS

#app/assets/javascripts/application.js
$(document).ready( function() {

    var base_url = window.location.protocol + "//" + window.location.host;

    $('#SearchSearch').searchbox({
        url: base_url + '/search/',
        param: 'search',
        dom_id: '#livesearch',
        loading_css: '#livesearch_loading'
    })      
});

Upvotes: 4

Mateusz Urbański
Mateusz Urbański

Reputation: 7862

I do it in other way. I've add code that submits the form after checkbox click.

 $('#search-filters input[type=checkbox]').click ->
    $(this).closest("form").submit()

Now everything is working fine.

Upvotes: 1

Himesh
Himesh

Reputation: 646

Follow these steps:

  1. Make an AJAX call on checkbox change event.
  2. Render partial containing the search results that you want to show on front-end from the controller.
  3. Replace the search results in the front-end using javascript inside success function of AJAX request!

This should solve your problem! :)

Upvotes: 2

Related Questions