Skip to content

AJAX results loading

Jonathan Rochkind edited this page Jul 1, 2021 · 11 revisions

Delayed results loading via AJAX (actually more like AJAH html)

BentoSearch provides some basic support for initially displaying a placeholder progress spinner, and having Javascript call back to get the actual results. The built-in JS code requires JQuery.

You can also, of course, write your own functionality for this in your own app using more primitive bento_search building blocks, no need to fight with this functionality if it's not doing what you need.

What is AJAX loading good for?

It is not good for search engines with extremely/pathologically slow response times. The AJAX functionality at present merely does an ordinary HTTP request that will block for full time needed to retrieve results -- meaning in most current Rails deployment environments, it will block the app process for the entire time, so it's not a solution for 15-second retrieval times or something.

But it can be helpful in some scenarios anyway, use your judgement. The manually triggered AJAX load, for instance, can be helpful if you have a search tool with request rate limits, and you only want to retrieve results when triggered by a user click.

Be careful of access auth issues -- opt in

The built-in AJAX loading functionality is powered by the BentoSearch::SearchController, which can delivery 'snippets' of partial HTML search results. Once this is available for a certain engine, someone could reverse engineer it to view results skipping your application's usual authorization/access control (unless you've configured access control in the SearchController too, see below).

Thus, you have to opt-in on a per-engine basis to let SearchController know it's okay to serve the HTML results -- as well as configuring routing for the SearchController in general.

Configuration to Enable Feature

  • In your ./config/routes.rb, you need BentoSearch::Routes.new(self).draw in order to route to the ajax loader.

  • An engine you want to have AJAX-loaded results needs to be globally registered with BentoSearch.register_engine, and needs to have been configured with allow_routable_results = true

        BentoSearch.register_engine("gbs") do |conf|
          # ...
          conf.allow_routable_results = true
         end
    

Include the JS in your application

Our ajax load JS has a dependency on JQuery, but it isn't actually expressed formally. The code expects JQuery to be available at window.JQuery, sorry!

Sprockets

If you are using sprockets for your JS, just //= require 'bento_search/ajax_load to your app/assets/application.js file, to get the standard JS for ajax loading.

Webpacker

For use with Webpacker-managed JS, you can copy the JS file into your local app by running:

rails generate bento_search:install:ajax_load_js

Then add to your app/javascripts/packs/application.js file, require '../src/js/bento_search_ajax_load.js';

When you upgrade bento_search, if there have been changes to the JS code, you'll have to run rails generate bento_search:install:ajax_load_js after install to copy the new version into your local app.

Sorry, this is a little bit hacky way of providing support for webpacker, we'll see how it turns out.

Automatic loading of results

If you'd like search results to be loaded via AJAX, and the js AJAX request to load results to be executed immediately on document read, just use the bento_search helper method with the registered id of your configured engine, the search arguments, and a :load => :ajax_auto argument.

<%= bento_search("gbs", :query => "my query", 
                 :semantic_search_field => :title,
                 :load => :ajax_auto) %>

Triggered on-demand loading of results

If you want to set up a placeholder for ajax loading, but trigger it on some event (probably user-triggered) instead of immediately on document ready, you can use the the :ajax_triggered load mode instead:

<%= bento_search("gbs", :query => "my query", 
                 :semantic_search_field => :title,
                 :load => :ajax_triggered) %>

This will output a hidden (display:none) div with class bento_search_ajax_wait ready to receive the results; this div is used as an argument to the BentoSearch.ajax_load function (which you got from //= require 'bento_search/ajax_load in your asset pipeline).

For instance, to make a click on a certain hyperlink trigger the ajax loading of results in a placeholder div that's right after the hyperlink:

jQuery(document).ready(function($) {    
  $("a.my_class_to_trigger").click(function(event) {
    event.preventDefault();
    event.stopPropagation();        
    BentoSearch.ajax_load( $(this).next(".bento_search_ajax_wait") );
  });
});

You can also supply a callback function as the second argument to ajax_load; it will be called right before the HTML is actually placed on page; it will have this set to the placeholder div; it can take an argument consisting of the partial HTML. You can return false to prevent automatic placing of partial HTML on page, and do it yourself.

Access control issues

If the particular engine you are using for 'background' ajax loading does not need access controls of any kind, no problem, you're good.

But if it does, you need to make sure those access controls are applied by the controller that's delivering the partial HTML to drop into the page. That could mean refusing to deliver the results at all unless there is a logged in user, or applying different search arguments depending on presence or characteristics of logged in user.

We're still working out the best devices to support this, feel free to ask, or tell us your use case to help us develop the right support, or suggest an api.

Success callback for custom display

The AJAX loader supports a Javascript function callback on success, you can use to modify the display in custom ways.

Starting in bento_search 1.7, you can specify a default callback for all AJAX results loading calls:

# in javascript
BentoSearch.ajax_load.default_success_callback = function(div) {
   # ...
}

The callback will have this set to be the already-on-page container DOM element.

The div argument passed in is the new content about to be placed on the page -- it has not yet been placed on the page, so does not have a place in the DOM yet. This div will also contains an html5 meta tag with the total result count in it, looking like this: <meta itemprop="total_items" content="232323"/>. Your success callback may want to extract that value to place it somewhere on your page.

Returning false from the success callback will prevent default placement of the new content div on the page. You can use this if you want to do something custom instead.

If you want to send a different success callback to each AJAX call, at present you'd have to stop using the automatic AJAX loading from bento_results, and call the JS BentoSearch.ajax_load function yourself manually, supplying your callback(s).

To be documented

  • a wrapper partial can be specified in config