JHipster+Angular下拉框滚动加载更多用户数据求助
问题描述
在JHipster 7.9.3 + Angular 14.2.1项目中,User与UserProfile为一对一关联,创建/编辑UserProfile时需绑定对应用户。当前打开创建/编辑页面时,/api/users接口默认仅返回前20条数据,当用户数量超过20时,第21位及以后的用户无法在下拉选择框中显示。需保留下拉框滚动形式,实现滚动加载更多用户数据。
解决方案
前端修改(Angular)
原生<select>无法监听滚动事件实现加载更多,因此改用自定义下拉容器结合滚动加载逻辑:
1. 修改组件模板(user-profile-update-component.html)
替换原生select为自定义下拉结构,添加滚动监听和加载状态提示:
<div class="row mb-3"> <label class="form-label" for="field_user">User</label> <!-- 自定义下拉触发容器 --> <div class="form-control position-relative" (click)="toggleDropdown()" id="field_user" data-cy="user"> <span *ngIf="editForm.get('user')!.value">{{ editForm.get('user')!.value.login }}</span> <span *ngIf="!editForm.get('user')!.value" class="text-muted">请选择用户</span> <span class="position-absolute end-0 top-50 translate-middle-y me-2">▼</span> <!-- 下拉选项列表(带滚动) --> <div *ngIf="dropdownOpen" class="position-absolute top-100 start-0 end-0 bg-white border shadow-sm mt-1 max-h-40 overflow-auto" (scroll)="onDropdownScroll($event)"> <div *ngIf="loading" class="text-center p-2">加载中...</div> <div class="p-2 cursor-pointer" *ngFor="let userOption of usersSharedCollection" (click)="selectUser(userOption)" [class.bg-primary-subtle]="editForm.get('user')!.value?.id === userOption.id" > {{ userOption.login }} </div> <div *ngIf="hasMore && !loading" class="text-center p-2 text-muted">滚动加载更多</div> </div> </div> <input type="hidden" formControlName="user"> </div> <div *ngIf="editForm.get(['user'])!.invalid && (editForm.get(['user'])!.dirty || editForm.get(['user'])!.touched)"> <small class="form-text text-danger" *ngIf="editForm.get(['user'])?.errors?.required"> 此字段为必填项。 </small> </div>
2. 修改组件逻辑(user-profile-update-component.ts)
添加分页变量、滚动加载及下拉控制逻辑:
import { Component, OnInit } from '@angular/core'; import { HttpResponse } from '@angular/common/http'; import { ActivatedRoute } from '@angular/router'; import { Observable } from 'rxjs'; import { finalize, map } from 'rxjs/operators'; import { UserProfileFormService, UserProfileFormGroup } from './user-profile-form.service'; import { IUserProfile } from '../user-profile.model'; import { UserProfileService } from '../service/user-profile.service'; import { IUser } from 'app/entities/user/user.model'; import { UserService } from 'app/entities/user/user.service'; import { Pagination } from 'app/core/request/request.model'; @Component({ selector: 'jhi-user-profile-update', templateUrl: './user-profile-update.component.html', styles: [` .cursor-pointer { cursor: pointer; } .max-h-40 { max-height: 10rem; } `] }) export class UserProfileUpdateComponent implements OnInit { isSaving = false; userProfile: IUserProfile | null = null; usersSharedCollection: IUser[] = []; dropdownOpen = false; loading = false; currentPage = 0; pageSize = 20; hasMore = true; editForm: UserProfileFormGroup = this.userProfileFormService.createUserProfileFormGroup(); constructor( protected userProfileService: UserProfileService, protected userProfileFormService: UserProfileFormService, protected userService: UserService, protected activatedRoute: ActivatedRoute ) {} compareUser = (o1: IUser | null, o2: IUser | null): boolean => this.userService.compareUser(o1, o2); ngOnInit(): void { this.activatedRoute.data.subscribe(({ userProfile }) => { this.userProfile = userProfile; if (userProfile) { this.updateForm(userProfile); } // 加载第一页用户数据 this.loadUsersPage(0); }); } toggleDropdown(): void { this.dropdownOpen = !this.dropdownOpen; } selectUser(user: IUser): void { this.editForm.patchValue({ user }); this.dropdownOpen = false; } onDropdownScroll(event: Event): void { const target = event.target as HTMLElement; // 滚动到底部附近时加载下一页 if (target.scrollTop + target.clientHeight >= target.scrollHeight - 10 && this.hasMore && !this.loading) { this.currentPage++; this.loadUsersPage(this.currentPage); } } loadUsersPage(page: number): void { this.loading = true; const pagination: Pagination = { page, size: this.pageSize }; this.userService .query(pagination) .pipe(map((res: HttpResponse<IUser[]>) => res.body ?? [])) .subscribe({ next: (users) => { this.usersSharedCollection = this.userService.addUserToCollectionIfMissing<IUser>(this.usersSharedCollection, ...users); this.hasMore = users.length === this.pageSize; this.loading = false; }, error: () => this.loading = false }); } previousState(): void { window.history.back(); } save(): void { this.isSaving = true; const userProfile = this.userProfileFormService.getUserProfile(this.editForm); if (userProfile.id !== null) { this.subscribeToSaveResponse(this.userProfileService.update(userProfile)); } else { this.subscribeToSaveResponse(this.userProfileService.create(userProfile)); } } protected subscribeToSaveResponse(result: Observable<HttpResponse<IUserProfile>>): void { result.pipe(finalize(() => this.onSaveFinalize())).subscribe({ next: () => this.onSaveSuccess(), error: () => this.onSaveError(), }); } protected onSaveSuccess(): void { this.previousState(); } protected onSaveError(): void {} protected onSaveFinalize(): void { this.isSaving = false; } protected updateForm(userProfile: IUserProfile): void { this.userProfile = userProfile; this.userProfileFormService.resetForm(this.editForm, userProfile); if (userProfile.user) { this.usersSharedCollection = this.userService.addUserToCollectionIfMissing<IUser>(this.usersSharedCollection, userProfile.user); } } }
后端修改(Java)
后端接口已支持分页,需确保分页参数能正常传递:
检查PublicUserResource中的参数校验方法
确保onlyContainsAllowedProperties方法允许分页排序的合法字段,示例如下:
private boolean onlyContainsAllowedProperties(Pageable pageable) { Set<String> allowedProperties = Set.of("id", "login", "firstName", "lastName"); return pageable.getSort().stream() .allMatch(order -> allowedProperties.contains(order.getProperty())); }
该方法用于限制排序字段的范围,避免非法排序请求,需根据业务需求调整允许的字段列表。
内容的提问来源于stack exchange,提问作者MiMoy
相关产品推荐
相关产品推荐

