Динамически вставляйте контент для полнотекстового поиска пользователя с помощью JS и Rails - PullRequest
0 голосов
/ 24 сентября 2018

У меня есть поиск input-field, и я хочу отображать содержимое динамически в зависимости от того, что пользователь вводит в него.

База данных имеет 1,5 тыс. Слотов, и я уже могу искать, задав параметр

?search_for=SOMETHING_TO_SEARCH_FOR

, где SOMETHING_TO_SEARCH_FOR - просто строка.Он выполняет полнотекстовый поиск по базе данных и дает мне результаты.

Я хотел бы заменить результаты поиска текущими отображаемыми элементами внутри

<div class = 'slots-container' id = 'dynamic'>...</div>

index.html.erb

<div class = "index-body">

  <%= link_to 'home', root_path, id: 'home', hidden: true %>

  <div class = "search_bar" id = 'search'>
    <input type="text" placeholder="Search..">
  </div>

  <div id = 'slots'>
    <div class = 'slots-container' id = 'dynamic'>
     <%= render @slots %>
    </div>
  </div>

  <%= will_paginate @slots, hidden: true %>

</div>

index.js.erb

добавляет следующие элементы в div с помощью id: dynamic иудаляет .pagination div, который включается в оператор will_paginate, если у нас больше нет страниц, так что мой скрипт для загрузки дополнительных страниц получает нулевую ссылку

$('#dynamic').append('<%= j render @slots %>');


<% unless @slots.next_page %>
  $('.pagination').remove();
<% end %>

main.js

Загружает новую страницу, если Пользователь находится на расстоянии 100px от конца документа,

с url: next_page от ссылки с .next классом

var ready = true;
$( document ).on('turbolinks:load', function() {
  $("img").lazyload();
  document.onscroll = function(){loadNextPage()};
})

function loadNextPage(){
  var window_top = $(window).scrollTop();
  var doc_height = $(document).height();
  var window_height = $(window).height();
  var window_bottom = window_top + window_height;
  var should_scroll = doc_height - window_bottom < 100; 
  var next_page = $('.next_page').attr('href');

  if (ready && should_scroll && next_page) {
    ready = false;
    $.getScript(next_page).done(function() {
      $("img").lazyload();
      ready = true;
    });
  }
}

function loadSearch(){
  var root_page = $('#home').attr('href');
  ...
}

Как вы видите, моя функция loadSearch() должна установить параметр search_for и добавить его к моему root_page url, а мой index.js.erb должен заменить текущее содержимое документа результатами поиска, а также иметь возможностьвсе еще выполняйте бесконечную функцию прокрутки.

Я думаю, что контроллер не представляет интереса, но обязательношляпа @slots имеет правильные элементы пагина, которые нужно нарисовать

1 Ответ

0 голосов
/ 26 сентября 2018

Теперь я решил это следующим образом:

index.html.erb

<div class = "index-body">

  <%= link_to 'home', root_path, id: 'home', hidden: true %>

  <div class = "search-bar">
    <input class = "search-input" type="text"
                    placeholder="Search..."
                    id = "search-input">
  </div>

  <div id = 'slots-container'>

    <div class = 'slots-container' id = 'slots'>
      <%= render @slots %>
      <%= will_paginate @slots, hidden: true,id: "paginate" %>
    </div>

  </div>

</div>

index.js.erb

@identical_search указывает, что последний результат поиска совпадает с текущим, поэтому ничего не делать

<% unless @identical_search %>
  if (dirty) {
    dirty = false;
    $('#slots').empty();
  }

  $('#slots').append('<%= j render @slots %>');
  <% if @slots.next_page %>
    if(document.getElementById("paginate") === null)
      $('#slots').append('<%= j will_paginate @slots, hidden: true, id: "paginate" %>');

    $('#paginate').replaceWith('<%= j will_paginate @slots, hidden: true, id: "paginate" %>');
  <% else %>
    $('#paginate').remove();
  <% end %>

<% end %>

main.js

var documentLoaded = false;
var dirty = false;
var pending = false;
var loadSearchTimer = null;
$( document ).on('turbolinks:load', function() {
  ...
  documentLoaded = true;
  document.getElementById("search-input").addEventListener("input", startTimerForUserInput);

})

...

function startTimerForUserInput(){
  if (loadSearchTimer !==null)
    clearTimeout(loadSearchTimer);
  loadSearchTimer = setTimeout(loadSearch, 300);
}

function loadSearch(){
  if (documentLoaded){
    documentLoaded = false;
    dirty=true;
    var search_input = document.getElementById("search-input");
    var search_for = search_input.value;
    var root_page = $('#home').attr('href');
    if(search_for && search_for !== '')
      root_page += "?search_for=" + search_for;
    $.getScript(root_page).done(function() {
      $("img").lazyload();
      documentLoaded = true;
      if(pending){
        pending = false;
        loadSearch();
      }
    });
  }
  else
    pending = true;
}

В main.js Я использую таймер для поиска только через 300 мс после последнего пользовательского ввода, чтобы ограничить вызовы, чтобы он не осуществлял поиск после каждого ввода.Когда getScript возвращает success, я проверяю, был ли отправлен запрос на поиск, в то время как текущий запрос обрабатывался для отправки нового запроса с помощью переменной pending.

dirty устанавливается в значение true, когдапользовательский поиск, так что index.js.erb знает, чтобы очистить div, содержащий элементы.

Это проходит гладко, и мне нравится, как это.

Я надеюсь, что это поможет кому-то в будущем...

Если это помогает понять всю картину целиком, вот контроллер:

class SlotsController < ApplicationController
  require 'will_paginate/array'
  @@last_search_content = nil

  def index
    @identical_search = false
    @hashtags = Hashtag.all
    if params[:search_for]
      search_str = params[:search_for].gsub(/\s\s+/, ' ').strip
      @slots = Slot.where("LOWER(slot_name) LIKE LOWER('%#{search_str}%') ")
      if @@last_search_content.to_set == @slots.to_set && (params[:page] == nil || params[:page] == 1)
        @identical_search = true
      end
    elsif params[:hashtags]
      @slots = slotsWithAtLeastOneOfThose(params[:hashtags])
    else 
      @slots = Slot.all
    end
    @@last_search_content = @slots
    unless @identical_search
      @slots = @slots.paginate(page: params[:page])
    end

    respond_to do |format|
      format.html
      format.js
    end
  end

  def show
    @slot = Slot.find(params[:id])
  end

  private

    def slotsWithAtLeastOneOfThose(hashtags)
      slots=[]
      hashtags.split(' ').each do |h|
        slots += Slot.joins(:hashtags).where("hashtags.value LIKE ?", "%#{h}%")
      end
      return slots.uniq
    end
end
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...