Selecting date causes page reload when AJAX is enabled

Created on 5 April 2022, about 3 years ago
Updated 28 September 2023, over 1 year ago

Problem/Motivation

When the view of search results has AJAX enabled, using other facets (such as checkboxes) prompts an AJAX reload, but using the date range facets prompts a full page reload.

Steps to reproduce

  • Basic setup:
    • Set up a fresh Drupal 9 site with facets_date_range enabled.
    • Set up a content type with a date field and a boolean field.
    • Create a few nodes with different field values.
    • Set up a database search server, search index, and view of the search index with a page display.
    • Set up a Date Range facet for the date field and a checkbox facet for the boolean field.
    • Add the facet blocks to the search page.
  • Key steps to reproduce the bug:
    • In the view of the search index, set Advanced > Other > Use AJAX to Yes.
    • On the search page, check a box for the boolean facet.
      • A spinner appears and the content updates (good).
    • Now select a date in the date range facet.
      • No spinner. Firefox's developer tools (Network tab) shows that a request occurred.

Proposed resolution

Maybe use Drupal.ajax(…).execute() and window.history.pushState(…) like in facets/js/facets-views-ajax.js?

🐛 Bug report
Status

Active

Version

1.0

Component

Code

Created by

🇺🇸United States jlstrecker Athens, Ohio, USA

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Comments & Activities

Not all content is available!

It's likely this issue predates Contrib.social: some issue and comment data are missing.

  • 🇮🇹Italy reinchek Napoli 🌋, 🇮🇹

    Hi, i'm having same issue.
    As you said the problem is caused by:
    window.location.href = facetUrl.replace("__date_range_min__", min).replace("__date_range_max__", max);
    causing the full page reload.

    For now i've resolved (look at the attached patch) this by calling, inside the autoSubmit() function, the function updateFacetsView() of `Drupal.behaviors.facetsViewsAjax` library. But to do this it's also needed to move the "Helper funciton" updateFacetsView() inside the Drupal.behaviors.facetsViewsAjax object.

    For the sake of completeness, I enclose the changes made within `facets/js/facets-views-ajax.js` (drupal/facets:1.6.0):

    diff --git a/modules/contrib/facets/js/facets-views-ajax.js b/modules/contrib/facets/js/facets-views-ajax.js
    --- a/modules/contrib/facets/js/facets-views-ajax.js
    +++ b/modules/contrib/facets/js/facets-views-ajax.js	(date 1695895680257)
    @@ -17,6 +17,7 @@
        */
       Drupal.behaviors.facetsViewsAjax = {
         attach: function (context, settings) {
    +      var helpers = this;
    
           // Loop through all facets.
           $.each(settings.facets_views_ajax, function (facetId, facetSettings) {
    @@ -38,10 +39,10 @@
             }
    
             // Update view on summary block click.
    -        if (updateFacetsSummaryBlock() && (facetId === 'facets_summary_ajax')) {
    +        if (helpers.updateFacetsSummaryBlock() && (facetId === 'facets_summary_ajax')) {
               $('[data-drupal-facets-summary-id=' + facetSettings.facets_summary_id + ']').find('a').once('facets_summary_ajax_link').click(function (e) {
                 e.preventDefault();
    -            updateFacetsView($(this).attr('href'), current_dom_id, view_path);
    +            helpers.updateFacetsView($(this).attr('href'), current_dom_id, view_path);
                 // Remove clicked element, ajax callback will update the content.
                 $(this).parents('li').remove();
               });
    @@ -54,112 +55,126 @@
                   $(facet_item).on('facets_filter.facets', function (event, url) {
                     $('.js-facets-widget').trigger('facets_filtering');
    
    -                updateFacetsView(url, current_dom_id, view_path);
    +                helpers.updateFacetsView(url, current_dom_id, view_path);
                   });
                 }
               });
    
             }
           });
    -    }
    -  };
    +    },
    
    -  // Helper function to update views output & Ajax facets.
    -  var updateFacetsView = function (href, current_dom_id, view_path) {
    -    // Refresh view.
    -    var views_parameters = Drupal.Views.parseQueryString(href);
    -    var views_arguments = Drupal.Views.parseViewArgs(href, 'search');
    -    var views_settings = $.extend(
    -        {},
    -        Drupal.views.instances['views_dom_id:' + current_dom_id].settings,
    -        views_arguments,
    -        views_parameters
    -    );
    +    // Helper function to update views output & Ajax facets.
    +    updateFacetsView: function (href, current_dom_id, view_path) {
    +      // Refresh view.
    +      var views_parameters = Drupal.Views.parseQueryString(href);
    +      var views_arguments = Drupal.Views.parseViewArgs(href, 'search');
    +      var views_settings = $.extend(
    +          {},
    +          Drupal.views.instances['views_dom_id:' + current_dom_id].settings,
    +          views_arguments,
    +          views_parameters
    +      );
    
    -    // Update View.
    -    var views_ajax_settings = Drupal.views.instances['views_dom_id:' + current_dom_id].element_settings;
    -    views_ajax_settings.submit = views_settings;
    -    views_ajax_settings.url = view_path + '?q=' + href;
    +      // Update View.
    +      var views_ajax_settings = Drupal.views.instances['views_dom_id:' + current_dom_id].element_settings;
    +      views_ajax_settings.submit = views_settings;
    +      views_ajax_settings.url = view_path + '?q=' + href;
    
    -    Drupal.ajax(views_ajax_settings).execute();
    +      Drupal.ajax(views_ajax_settings).execute();
    
    -    // Update url.
    -    window.historyInitiated = true;
    -    window.history.pushState(null, document.title, href);
    +      // Update url.
    +      window.historyInitiated = true;
    +      window.history.pushState(null, document.title, href);
    
    -    // ToDo: Update views+facets with ajax on history back.
    -    // For now we will reload the full page.
    -    window.addEventListener("popstate", function (e) {
    -      if (window.historyInitiated) {
    -        window.location.reload();
    -      }
    -    });
    +      // ToDo: Update views+facets with ajax on history back.
    +      // For now we will reload the full page.
    +      window.addEventListener("popstate", function (e) {
    +        if (window.historyInitiated) {
    +          window.location.reload();
    +        }
    +      });
    
    -    // Refresh facets blocks.
    -    updateFacetsBlocks(href);
    -  }
    +      // Refresh facets blocks.
    +      this.updateFacetsBlocks(href);
    +    },
    
    -  // Helper function, updates facet blocks.
    -  var updateFacetsBlocks = function (href) {
    -    var settings = drupalSettings;
    -    var facets_blocks = facetsBlocks();
    +    // Helper function, updates facet blocks.
    +    updateFacetsBlocks: function (href) {
    +      var settings = drupalSettings;
    +      var facets_blocks = this.facetsBlocks();
    
    -    // Remove All Range Input Form Facet Blocks from being updated.
    -    if(settings.facets && settings.facets.rangeInput) {
    -      $.each(settings.facets.rangeInput, function (index, value){
    -        delete facets_blocks[value.facetId];
    -      });
    -    }
    +      // Remove All Range Input Form Facet Blocks from being updated.
    +      if(settings.facets && settings.facets.rangeInput) {
    +        $.each(settings.facets.rangeInput, function (index, value){
    +          delete facets_blocks[value.facetId];
    +        });
    +      }
    
    -    // Update facet blocks.
    -    var facet_settings = {
    -      url: Drupal.url('facets-block-ajax'),
    -      submit: {
    -        facet_link: href,
    -        facets_blocks: facets_blocks
    -      }
    -    };
    +      // Update facet blocks.
    +      var facet_settings = {
    +        url: Drupal.url('facets-block-ajax'),
    +        submit: {
    +          facet_link: href,
    +          facets_blocks: facets_blocks
    +        }
    +      };
    
    -    // Update facets summary block.
    -    if (updateFacetsSummaryBlock()) {
    -      facet_settings.submit.update_summary_block = true;
    -      facet_settings.submit.facet_summary_plugin_id = $('[data-drupal-facets-summary-id=' + settings.facets_views_ajax.facets_summary_ajax.facets_summary_id + ']').data('drupal-facets-summary-plugin-id');
    -      facet_settings.submit.facet_summary_wrapper_id = settings.facets_views_ajax.facets_summary_ajax.facets_summary_id;
    -    }
    +      // Update facets summary block.
    +      if (this.updateFacetsSummaryBlock()) {
    +        facet_settings.submit.update_summary_block = true;
    +        facet_settings.submit.facet_summary_plugin_id = $('[data-drupal-facets-summary-id=' + settings.facets_views_ajax.facets_summary_ajax.facets_summary_id + ']').data('drupal-facets-summary-plugin-id');
    +        facet_settings.submit.facet_summary_wrapper_id = settings.facets_views_ajax.facets_summary_ajax.facets_summary_id;
    +      }
    
    -    Drupal.ajax(facet_settings).execute();
    -  };
    +      Drupal.ajax(facet_settings).execute();
    +    },
    
    -  // Helper function to determine if we should update the summary block.
    -  // Returns true or false.
    -  var updateFacetsSummaryBlock = function () {
    -    var settings = drupalSettings;
    -    var update_summary = false;
    +    // Helper function to determine if we should update the summary block.
    +    // Returns true or false.
    +    updateFacetsSummaryBlock: function () {
    +      var settings = drupalSettings;
    +      var update_summary = false;
    
    -    if (settings.facets_views_ajax.facets_summary_ajax) {
    -      update_summary = true;
    -    }
    +      if (settings.facets_views_ajax.facets_summary_ajax) {
    +        update_summary = true;
    +      }
    
    -    return update_summary;
    -  };
    +      return update_summary;
    +    },
    +
    
    -  // Helper function, return facet blocks.
    -  var facetsBlocks = function () {
    -    // Get all ajax facets blocks from the current page.
    -    var facets_blocks = {};
    +    // Helper function, return facet blocks.
    +    facetsBlocks: function () {
    +      // Get all ajax facets blocks from the current page.
    +      var facets_blocks = {};
    
    -    $('.block-facets-ajax').each(function (index) {
    -      var block_id_start = 'js-facet-block-id-';
    -      var block_id = $.map($(this).attr('class').split(' '), function (v, i) {
    -        if (v.indexOf(block_id_start) > -1) {
    -          return v.slice(block_id_start.length, v.length);
    -        }
    -      }).join();
    -      var block_selector = '#' + $(this).attr('id');
    -      facets_blocks[block_id] = block_selector;
    -    });
    +      $('.block-facets-ajax').each(function (index) {
    +        var block_id_start = 'js-facet-block-id-';
    +        var block_id = $.map($(this).attr('class').split(' '), function (v, i) {
    +          if (v.indexOf(block_id_start) > -1) {
    +            return v.slice(block_id_start.length, v.length);
    +          }
    +        }).join();
    +        var block_selector = '#' + $(this).attr('id');
    +        facets_blocks[block_id] = block_selector;
    +      });
    
    -    return facets_blocks;
    +      return facets_blocks;
    +    },
    +
    +    // Helper function to add exposed form data to facets url
    +    addExposedFiltersToFacetsUrl: function (href, view_name, view_display_id) {
    +      var $exposed_form = $('form#views-exposed-form-' + view_name.replace(/_/g, '-') + '-' + view_display_id.replace(/_/g, '-'));
    +
    +      var params = Drupal.Views.parseQueryString(href);
    +
    +      $.each($exposed_form.serializeArray(), function () {
    +        params[this.name] = this.value;
    +      });
    +
    +      return href.split('?')[0] + '?' + $.param(params);
    +    }
       };
    
       /**
    @@ -188,26 +203,13 @@
           });
    
           if (reload) {
    -        href = addExposedFiltersToFacetsUrl(href, options.extraData.view_name, options.extraData.view_display_id);
    -        updateFacetsBlocks(href);
    +        href = this.addExposedFiltersToFacetsUrl(href, options.extraData.view_name, options.extraData.view_display_id);
    +        this.updateFacetsBlocks(href);
           }
         }
    
         // Call the original Drupal method with the right context.
         beforeSend.apply(this, arguments);
    -  }
    -
    -  // Helper function to add exposed form data to facets url
    -  var addExposedFiltersToFacetsUrl = function (href, view_name, view_display_id) {
    -    var $exposed_form = $('form#views-exposed-form-' + view_name.replace(/_/g, '-') + '-' + view_display_id.replace(/_/g, '-'));
    -
    -    var params = Drupal.Views.parseQueryString(href);
    -
    -    $.each($exposed_form.serializeArray(), function () {
    -      params[this.name] = this.value;
    -    });
    -
    -    return href.split('?')[0] + '?' + $.param(params);
       };
    
     })(jQuery, Drupal);
    
  • 🇮🇹Italy reinchek Napoli 🌋, 🇮🇹
  • 🇮🇹Italy reinchek Napoli 🌋, 🇮🇹

    I've made i mistake forgotting to say that this patch works only if the patch " #3273136 - Selecting date clears other facet values when using AJAX " was previous applied.

    I've rewrited the patch removing some console.log.

  • 🇮🇹Italy reinchek Napoli 🌋, 🇮🇹

    Sorry for the shitpatch... but i'm doing these changes while working... I've added in this new patch a fix.

    PS: I've hided bad patches and leaved visible only the correct one.

  • Status changed to Needs review 9 months ago
  • 🇺🇸United States jrb Raleigh-Durham Area, NC, USA

    This attached patch makes the Date Range Facet widget work correctly with Ajax. It does not require the patch in #3273136: Selecting date clears other facet values when using AJAX and should fix that issue as well.

    It uses the trigger() method with the facets_filter event, the same way the other Facets widgets do. I believe this is the recommended way to make Facets work with JavaScript / Ajax.

  • 🇷🇸Serbia miksha

    Thank you @jrb for the patch. I tested it but encountered an issue where "facetId" isn't defined. I added a bit of delays in your logic and now works fine for me. Your original patch was throwing some errors and blocked further facets usage:

    VM6402 date-range.js:23 Uncaught TypeError: Cannot read properties of undefined (reading 'url')
    at autoSubmit (VM6402 date-range.js:23:42)
    at HTMLInputElement. (VM6402 date-range.js:32:11)
    at HTMLInputElement.dispatch (VM6272 jquery.min.js:2:40035)
    at v.handle (VM6272 jquery.min.js:2:38006)

  • 🇷🇸Serbia miksha

    Actually I had an issue with the previous approach as it was disabling 'blur' so when one would enter date with keyboard and right after entering the year stop using keyboard (so no pressing ENTER or TAB) but instead clicking on another facet it would cause throwing console errors and date range facet wouldn't have been applied. This would later cause drupal core ajax logic like applySoftLimit etc. to fail. I attach a revised patch that uses a bit different approach but still supports AJAX. I tested and for me it works in all cases, using mouse (date picker) or keyboard.

Production build 0.71.5 2024