
Probably every web developer faced with the need to implement a search on the site. Quite a common solution - Apache Solr. In the world of Drupal development, this is no exception. For the integration of Solr with Drupal and the implementation of faceted search, there are modules
search_api ,
search_api_solr and
facetapi . But in most cases, we would like the search results and the facet filters to be updated without reloading the page, that is, ajax. And, as usual in the Drupal world, on
d.org there is some time and user-tested module (or maybe not proven, as lucky) that does what we need. In this case,
ajax_facets .
Ajax facets is a module that provides several types of widgets that can be used in faceted search filters. These are ârange sliderâ, âmultiple checkboxesâ, âselectboxâ and âlinksâ. When values ââin these âwidgetsâ change, the filters and the search result are updated with ajax. Great. But it would be even better if the module were friends with the history API. That is, it would save every filter state in the history, which would allow users to go through the search history with the back and forth buttons in the browser, again, without reloading the page.
Task
Of course, the need for this feature and the interest in implementation did not arise by itself. One of the projects was tasked with making ajax_facets friends with the history API. What I want to tell you.
')
Decision
As is usually the case, solving a problem begins with finding a complete solution or at least a patch. There was no ready solution, but
a patch was found . Judging
by the description on the issue tracker of the project, he did just what he needed. But, unfortunately, the patch was old and valid only for the old branch of the module (7.x-2.x). The idea is very simple: save the current state of the filters to the browser history at the moment when ajax_facets receives a successful response from the server to update the search result and the filters themselves. And by clicking on the âbackâ and âforwardâ buttons, retrieve the saved state of filters from the history and send a request to update the filters and search results with parameters from the saved state.
To test the idea as such, I
ported the found patch to the current module branch (7.x-3.x). Everything worked. However, it required improvements. Namely, I would like this feature to work in old browsers that do not support the history API. The task is simple. There is a
history.js that emulates the history API. On the other hand, I didnât want to add a hard dependency on this library, since this would mean adding a
libraries module to the dependencies. Such a patch just no one would have accepted. Imagine, you update the ajax_facets module, and in dependencies it has got libraries, which you donât need. You donât need the support of old browsers in the form of history.js either (you simply donât support old browsers, for example). To avoid such situations, I decided to make everything a bit more flexible:
- On the server side, we check the availability of the libraries module and the history.js library. If dependencies are found, then we pass the flag âhistory.js is available to the front-end side; you can use the history APIâ.
- On the client side, we check if the browser supports the history API (natively or through history.js). If yes, then we do everything as expected. Otherwise, we get the standard behavior of ajax_facets (as it was before the patch).
Implementation
The first item is achieved as follows:
We give out hints on the âStatus reportâ page if the dependencies are not found.
function ajax_facets_requirements($phase) { $requirements = array(); $t = get_t(); switch ($phase) { case 'runtime': $description = $t('For now browser ajax history feature works only in HTML5 browsers. If you want to get this feature on HTML4 browsers you need to install libraries module and download history.js library.'); $value = $t('Libraries module not installed.'); if (module_exists('libraries')) { if (!libraries_get_path('history.js')) { $description = $t('For now browser ajax history feature works only in HTML5 browsers. If you want to get this feature on HTML4 browsers you need to download history.js library.'); $value = $t('Library history.js not found.'); } else { $description = $t('For now browser ajax history feature works both in HTML4 and HTML5 browsers.'); $value = $t('Works with history.js library'); } } $requirements['ajax_facets_message'] = array( 'title' => $t('Ajax Facets'), 'description' => $description, 'value' => $value, 'severity' => REQUIREMENT_INFO, ); break; } return $requirements; }
And we forward the flag to the front-end side in case history.js library is found.
function ajax_facets_add_ajax_js($facet) { static $included = FALSE; if (!$included) { ...
The implementation of the second item is shown on the example of the pushState wrapper function:
Drupal.ajax_facets.pushState = function (state, title, stateUrl) {
By the way, there is one interesting feature in history.js that needs to be taken into account: the
statechange event
is triggered when the browser history buttons are pressed, as well as when the history is programmatically updated, for example by calling the History.pushState () method. In the native implementation of the history API by browsers, there is an
onpopstate event that is triggered only when clicking on the browser history buttons. To avoid unnecessary triggering of the statechange, you need to unsubscribe from this event before updating the browser history, and then subscribe to it again.
Conclusion
It is not always possible to find and apply a ready-made solution. But this is very cool. This makes it possible to understand, to see how the module that was used before, works inside. And, in the end, it's just nice when the solution you proposed is commited to a popular project. This means the next time someone else will not have such a problem.
Full diff can be viewed
here .