我试图实现无限滚动在鸡尾酒配方应用程序使用Rails 7,刺激/涡轮和Kaminari宝石分页。当我简单地过滤从控制器发送的所有记录时,所有的东西都会正常工作。也就是说,当我向下滚动页面时,页码正确地增加,每一个新的记录页都会按正确的顺序更新。
然而,当我向控制器发送过滤或排序参数时,这种行为就会变得不太可靠。新的记录页继续从控制器发送,但它会发送两到三个重复的页面,或者提前几页将页码提前几页。
我花了太多时间试图找出我在这里做错了什么,如果有一个仁慈的灵魂知道我错过了什么愚蠢的东西,我会很感激的。
这是我的鸡尾酒系列controller#index,在这里,我根据排序选项(所有成分、任何成分等)、鸡尾酒类别和/或一组需要过滤的特定成分(金币、薄荷、Applejack等)提供记录。
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这是我的分页刺激控制器:
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:
<%= turbo_stream.append 'recipes' do %>
<% @recipes.each do |recipe| %>
<%= render partial: "recipe_card", locals: { recipe: recipe } %>
<% end %>
<% end %>最后,我正在追加新记录的目标div:
<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页(不一定是按照顺序),开始加载到滚动。我觉得我可能错过了什么明显的东西。
发布于 2022-11-02 01:33:41
万一其他人遇到类似的问题,我想出了办法。可能是菜鸟犯的错误。
这个问题来自于我定位数据分页属性的位置。最初,我将它们放在一个div中,在将排序或筛选选项发送到控制器时替换它。部分是这样的:
<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:
<%# 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函数,以在每次选择新的排序选项时重置数据分页-页面值。
https://stackoverflow.com/questions/74239025
复制相似问题