首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >无法使用Rails 7、sorting、Kaminari gem和过滤/排序功能实现无限滚动

无法使用Rails 7、sorting、Kaminari gem和过滤/排序功能实现无限滚动
EN

Stack Overflow用户
提问于 2022-10-28 17:38:23
回答 1查看 29关注 0票数 0

我试图实现无限滚动在鸡尾酒配方应用程序使用Rails 7,刺激/涡轮和Kaminari宝石分页。当我简单地过滤从控制器发送的所有记录时,所有的东西都会正常工作。也就是说,当我向下滚动页面时,页码正确地增加,每一个新的记录页都会按正确的顺序更新。

然而,当我向控制器发送过滤或排序参数时,这种行为就会变得不太可靠。新的记录页继续从控制器发送,但它会发送两到三个重复的页面,或者提前几页将页码提前几页。

我花了太多时间试图找出我在这里做错了什么,如果有一个仁慈的灵魂知道我错过了什么愚蠢的东西,我会很感激的。

这是我的鸡尾酒系列controller#index,在这里,我根据排序选项(所有成分、任何成分等)、鸡尾酒类别和/或一组需要过滤的特定成分(金币、薄荷、Applejack等)提供记录。

代码语言:javascript
复制
  def index
    @page = params[:page] || 1
    category_id = params[:categoryId]
    ingredient_ids = params[:ingredientIds] ? [*params[:ingredientIds]].map(&:to_i) : nil
    
    @recipes = Recipe.alphabetical.page(@page)

    case params[:sort_option]
    when ''
      params[:ingredientIds] ?
        @recipes = Recipe.alphabetical.filter_all_recipes(ingredient_ids, category_id).page(@page) :
        @recipes = Recipe.filter_all_by_category(category_id).page(@page)
      respond_to do |format|
        # needed to explicitly call formats: [:html] after adding turbo_stream option
        format.html { render partial: 'recipe_cards', formats: [:html] }
        format.turbo_stream
      end
    when 'All Recipes'
      params[:ingredientIds] ?
        @recipes = Recipe.alphabetical.filter_all_recipes(ingredient_ids, category_id).page(@page) :
        @recipes = Recipe.filter_all_by_category(category_id).page(@page)
      respond_to do |format|
        format.html { render partial: 'recipe_cards', formats: [:html] }
        format.turbo_stream
      end
    when 'Any Ingredient'
      params[:ingredientIds] ?
        @recipes = Recipe.alphabetical.match_any_subset(ingredient_ids, current_user.ingredients, category_id).page(@page) :
        @recipes = Recipe.alphabetical.match_any_ingredient(current_user.ingredients, category_id).page(@page)
      respond_to do |format|
        format.html { render partial: 'recipe_cards', formats: [:html] }
        format.turbo_stream
      end
    when 'All Ingredients'
      params[:ingredientIds] ?
        @possible_recipes = Recipe.match_all_subset(params[:recipeIds], ingredient_ids, category_id).page(@page) :
        @possible_recipes = Recipe.alphabetical.match_all_ingredients(current_user.ingredients, category_id).page(@page)
      respond_to do |format|
        format.html { render partial: 'recipe_cards', formats: [:html] }
        format.turbo_stream
      end
    end
  end

这是我的分页刺激控制器:

代码语言:javascript
复制
import { Controller } from "@hotwired/stimulus";

export default class extends Controller {

  // gets/sets record fetching flag
  static get fetching() { return this.fetching; }
  static set fetching(bool) {
    this.fetching = bool;
  } 

  // gets url and page number from target element
  static get values() { return {
    url: String,
    page: { type: Number, default: 1 },
  };}

  // adds the scroll event listener and sets fetching flag to false
  connect() {
    console.log("Pagination Controller Loaded");
    document.addEventListener('scroll', this.scroll);
    this.fetching = false;
  }

  // binds this to the controller rather than document
  initialize() {
    this.scroll = this.scroll.bind(this);
  }

  // calls loadRecords() when scroll reaches the bottom of the page
  scroll() {
    if (this.pageEnd && !this.fetching) {
      this.loadRecords(); 
    }
  }

  // record fetching function
  async loadRecords() {
    // get pre-configured url from helper method
    const url = getUrl(this.urlValue, this.pageValue);
    
    // sets fetching flag to true
    this.fetching = true;

    // sends a turbo_stream fetch request to the recipes controller
    await fetch(url.toString(), {
      headers: {
        Accept: 'text/vnd.turbo-stream.html',
      },
    }).then(r => r.text())
      .then(html => Turbo.renderStreamMessage(html));

    // sets fetching flag to false
    this.fetching = false;

    // increments the target element's 
    this.pageValue += 1;
  }

  // sets the boundary where the loadRecords() function gets called
  get pageEnd() {
    const { scrollHeight, scrollTop, clientHeight } = document.documentElement;
    return scrollHeight - scrollTop - clientHeight < 40; // can adjust to desired limit
  }
}

//  -------------  HELPER FUNCTIONS ----------------

// gets selected ingredient ids from liquor cabinet display
// options and returns them in an array
function getIngredientIds() {
  var ingredientIds = [...$('.cabinet-spirits').val(),
  ...$('.cabinet-modifiers').val(),
  ...$('.cabinet-sugars').val(),
  ...$('.cabinet-garnishes').val()];
  return ingredientIds;
}

// if there are ingredientIds, appends them as an array to searchParams
function appendIngredientIds(url) {
  var ingredientIds = getIngredientIds();
  if (ingredientIds.length != 0) {
    ingredientIds.map(i => url.searchParams.append('ingredientIds', i));
  }
  return url;
}

// configures url searchParams and returns the url
function getUrl(urlValue, pageValue) {
  var url = new URL(urlValue);
  url.searchParams.set('page', pageValue);
  url.searchParams.append('sort_option', $('.sort-options').val());
  url = appendIngredientIds(url);
  return url;
}

下面是index.turbo.erb:

代码语言:javascript
复制
<%= turbo_stream.append 'recipes' do %>
  <% @recipes.each do |recipe| %>
    <%= render partial: "recipe_card", locals: { recipe: recipe } %>
  <% end %>
<% end %>

最后,我正在追加新记录的目标div:

代码语言:javascript
复制
<div class="container-fluid mt-2 mx-3">
  
  <div id="recipes" class="row row-cols-lg-5 row-cols-md-4 row-cols-sm-3 g-2" 
                    data-controller='pagination'
                    data-pagination-target='recipes'
                    data-pagination-url-value='<%= recipes_url %>'
                    data-pagination-page-value='<%= 2 %>'>

    <% @recipes.each do |recipe| %>
      <%= render partial: "recipe_card", locals: { recipe: recipe } %>
    <% end %>
  </div>
</div>

我已经监控了devTools中的页面增量,看起来对于食谱控制器的每一次额外的ajax调用,分页控制器都会被调用额外的时间。因此,如果我按“任何成分”对结果进行排序,我就会在滚动时得到重复的页面。如果我然后过滤波旁饮料的结果,3页(不一定是按照顺序),开始加载到滚动。我觉得我可能错过了什么明显的东西。

EN

回答 1

Stack Overflow用户

发布于 2022-11-02 01:33:41

万一其他人遇到类似的问题,我想出了办法。可能是菜鸟犯的错误。

这个问题来自于我定位数据分页属性的位置。最初,我将它们放在一个div中,在将排序或筛选选项发送到控制器时替换它。部分是这样的:

代码语言:javascript
复制
<div class="container-fluid mt-2 mx-3">
  
  <div id="recipes" class="row row-cols-lg-5 row-cols-md-4 row-cols-sm-3 g-2" 
                    data-controller='pagination'
                    data-pagination-target='recipes'
                    data-pagination-url-value='<%= recipes_url %>'
                    data-pagination-page-value='<%= 2 %>'>

    <% @recipes.each do |recipe| %>
      <%= render partial: "recipe_card", locals: { recipe: recipe } %>
    <% end %>
  </div>
</div>

如前所述,无限滚动/分页工作正常,直到将排序或筛选选项发送给控制器。之后,滚动函数开始按顺序添加重复的页面或页面。

当我将数据分页属性移动到包含部分的div时,不需要的行为就停止了。这是外部div:

代码语言:javascript
复制
 <%# cocktail card container %>
    <div class='col offset-2 recipe-cards me-3' 
         data-controller='pagination'
         data-pagination-target='recipes'
         data-pagination-url-value='<%= recipes_url %>'
         data-pagination-page-value='<%= 2 %>'>

      <%= render partial: 'recipe_cards', locals: { recipes: @recipes } %>
      
    </div>

我相信在部分更新之后数据属性被持久化,这样分页最终会响应多个目标。

我还需要创建一个简单的javascript函数,以在每次选择新的排序选项时重置数据分页-页面值。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/74239025

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档