-
-
Notifications
You must be signed in to change notification settings - Fork 4.1k
Description
Overview of the issue
When using the React framework for JHipster admin UI, the JHipster generated components for handling entity list views show bad search results.
The user sees bad items in the search list results, but the backend returns correct data - verified with the request response in the Network tab. This issue is related to using the list item order number value for the key attribute when rendering lists in the respective UI components, instead of using unique values like an entity id.
NOTE Issue seems to be entity model independent and related purely to how React handles list rendering, so the JHipster info is attached only for posterity and completeness.
JHipster info
[email protected] [***]
└── [email protected]
JHipster configuration, a .yo-rc.json file generated in the root folder
.yo-rc.json file
{
"generator-jhipster": {
"applicationType": "monolith",
"authenticationType": "oauth2",
"baseName": "backendServicesPrototype",
"buildTool": "maven",
"cacheProvider": "redis",
"clientFramework": "react",
"clientTestFrameworks": [],
"clientTheme": "darkly",
"clientThemeVariant": "dark",
"creationTimestamp": 1757786915003,
"databaseType": "sql",
"devDatabaseType": "postgresql",
"devServerPort": 9060,
"enableHibernateCache": true,
"enableTranslation": true,
"entities": [
"Photo",
"Tag",
"PhotoMetadata",
"Contest",
"ContestEntry",
"Award",
"TagCategory",
"TagCategoryI18N",
"UserProfile",
"Gallery",
"GalleryI18N",
"UserTag",
"UserEquipment"
],
"feignClient": null,
"jhipsterVersion": "8.11.0",
"languages": [
"pl",
"en"
],
"lastLiquibaseTimestamp": 1760134455000,
"microfrontend": null,
"microfrontends": [],
"nativeLanguage": "pl",
"packageName": "[***]",
"prodDatabaseType": "postgresql",
"reactive": false,
"searchEngine": "elasticsearch",
"serverPort": null,
"serviceDiscoveryType": null,
"skipUserManagement": true,
"syncUserWithIdp": true,
"testFrameworks": [
"gatling"
],
"withAdminUi": true
}
}
Environment and Tools
openjdk version "21.0.3" 2024-04-16 LTS
OpenJDK Runtime Environment Temurin-21.0.3+9 (build 21.0.3+9-LTS)
OpenJDK 64-Bit Server VM Temurin-21.0.3+9 (build 21.0.3+9-LTS, mixed mode)
git version 2.51.1
node: v22.15.1
npm: 10.9.2
Docker version 28.5.1, build e180ab8ab8
JDL for the Entity configuration(s) entityName.json files generated in the .jhipster directory
JDL entity definitions
/**
* Entity Photo - Photo
*/
@ChangelogDate("20250913181332")
entity Photo {
url String required
thumbnailUrl String required
contentType String required
createdAt Instant required
updatedAt Instant
width Integer required
height Integer required
license License required
isActive Boolean required
fileSize Long required
}
/**
* Entity Tag - Tag.\n\nTags have name and category. Combination of name and category is unique.
*/
@AddLiquibaseChangeSet("classpath:config/liquibase/changelog/20251005_add_unique_tag.xml")
@ChangelogDate("20250913181333")
entity Tag {
name String required minlength(2) maxlength(50)
}
/**
* Entity PhotoMetadata - Metadata of the photo.\n\nThis entity stores the metadata of the photo that was added to the service.\nMetadata for the purpose of a Contest is stored in a separate instance of\nPhotoMetadata as it can be customized for the purpose of the Contest.
*/
@ChangelogDate("20251009212632")
entity PhotoMetadata {
name String required minlength(2) maxlength(200)
description String maxlength(500)
timeOfDay TimeOfDay
season Season
district District
createdAt Instant required
updatedAt Instant
}
/**
* Entity Contest - Represents a monthly photo contest (or some other photo contest).
*/
@ChangelogDate("20251009212633")
entity Contest {
name String required minlength(2) maxlength(50)
description String maxlength(500)
startDate Instant required
endDate Instant required
status ContestStatus required
}
/**
* Entity ContestEntry - Contest entry.\n\nThis entity stores information on the photo used in the contest.
*/
@ChangelogDate("20251009212634")
entity ContestEntry {
votes Integer required
createdAt Instant required
updatedAt Instant
isActive Boolean required
}
/**
* Entity Award - Award.\n\nThis entity stores information about the award given to the photo used in the contest.
*/
@ChangelogDate("20251009212635")
entity Award {
type AwardType required
description String
}
/**
* Entity TagCategory - Tag category.\n\nThis entity stores information about the tag category. Initially it contains six categories.
*/
@ChangelogDate("20251009212636")
entity TagCategory {
code String required minlength(2) maxlength(50)
}
/**
* Entity TagCategoryI18N - Tag category translation.\n
*/
@ChangelogDate("20251009212637")
entity TagCategoryI18N (tag_category_i_18_n) {
name String required minlength(2) maxlength(50)
locale String required
}
/**
* Entity UserProfile - User profile.\n\nUser profile contains information extending standard user information stored in the built-in User entity.
*/
@ChangelogDate("20251009212638")
entity UserProfile {
nickName String minlength(2) maxlength(50)
type AccountType required
isActive Boolean required
description TextBlob
homepage String
}
/**
* Entity Gallery - Gallery.\n\nGallery is a collection of photos or sub galleries.\nIf a gallery has no parent, it can contain only sub galleries.\nOnly sub gallery can contain photos.
*/
@ChangelogDate("20251009212639")
entity Gallery {
isActive Boolean required
order Integer required
createdAt Instant required
updatedAt Instant
}
/**
* Entity GalleryI18N - Gallery translation.\n
*/
@ChangelogDate("20251009212640")
entity GalleryI18N (gallery_i_18_n) {
locale String required
name String required minlength(2) maxlength(50)
description String maxlength(500)
createdAt Instant required
updatedAt Instant
}
/**
* Entity UserTag - User tag.\n\nUser tag is a tag assigned to the user. It does not have any category like regular Tags.
*/
@ChangelogDate("20251010221414")
entity UserTag {
name String required minlength(2) maxlength(50)
}
/**
* Entity UserEquipment - User equipment.\n\nUser equipment contains information about the equipment used by the user.\nEach user can have multiple user equipment.
*/
@ChangelogDate("20251010221415")
entity UserEquipment {
name String required minlength(2) maxlength(200)
}
/**
* Enum License - License of the photo
*/
enum License {
CREATIVE_COMMONS,
PUBLIC_DOMAIN,
ALL_RIGHTS_RESERVED
}
/**
* Enum TimeOfDay - Time of day when the photo was taken
*/
enum TimeOfDay {
DAY,
NIGHT,
NOT_SPECIFIED
}
/**
* Enum Season - Season when the photo was taken
*/
enum Season {
SPRING,
SUMMER,
AUTUMN,
WINTER,
NOT_SPECIFIED
}
/**
* Enum District - District of Poznań where the photo was taken
*/
enum District {
JEZYCE (Jeżyce),
WILDA (Wilda),
DEBIEC (Dębiec),
WINOGRADY (Winogrady),
NARAMOWICE (Naramowice),
STAROLEKA (Starołęka),
STRZESZYN (Strzeszyn),
LAZARZ (Łazarz),
RATAJE (Rataje),
GRUNWALD (Grunwald),
STARE_MIASTO (Stare Miasto),
NOWE_MIASTO (Nowe Miasto),
PIATKOWO (Piątkowo),
KIEKRZ (Kiekrz),
LAWICA (Ławica),
GORCZYN (Górczyn),
SOLACZ (Sołacz)
}
/**
* Enum ContestStatus - Status of a photo contest
*/
enum ContestStatus {
PENDING,
ACTIVE,
ENDED
}
/**
* Enum AwardType - Type of the award\n\nAward type can be winner or honorable mention. All honorable mentions have the same priority.
*/
enum AwardType {
WINNER,
HONORABLE_MENTION
}
/**
* Enum AccountType - Type of the account\n\nAccount type can be private or media.
*/
enum AccountType {
PRIVATE,
MEDIA
}
relationship OneToOne {
Photo{baseMetadata} to PhotoMetadata
Contest{leadingPhoto} to Photo
ContestEntry{contestMetadata} to PhotoMetadata
UserProfile{user(login) required} to User with builtInEntity
Gallery{cover required} to Photo
}
relationship OneToMany {
TagCategory{translations} to TagCategoryI18N{tagCategory(code)}
Gallery{translations} to GalleryI18N{gallery}
}
relationship ManyToOne {
Photo{owner(login) required} to User with builtInEntity
Tag{category(code) required} to TagCategory
ContestEntry{photo} to Photo
ContestEntry{contest(name)} to Contest
Award{contestEntry} to ContestEntry{award(type)}
Gallery{parent} to Gallery
}
relationship ManyToMany {
PhotoMetadata{tags(name)} to Tag
UserProfile{tags(name)} to UserTag
UserProfile{equipment(name)} to UserEquipment
Gallery{photos} to Photo{galleries}
}
dto Photo, Tag, PhotoMetadata, Contest, ContestEntry, Award, TagCategory, TagCategoryI18N, UserProfile, Gallery, GalleryI18N, UserTag, UserEquipment with mapstruct
paginate Photo, Tag, PhotoMetadata, Contest, ContestEntry, Award, TagCategory, TagCategoryI18N, UserProfile, Gallery, GalleryI18N, UserTag, UserEquipment with pagination
service Photo, Tag, PhotoMetadata, Contest, ContestEntry, Award, TagCategory, TagCategoryI18N, UserProfile, Gallery, GalleryI18N, UserTag, UserEquipment with serviceClass
filter Photo, Tag, PhotoMetadata, Contest, ContestEntry, Award, TagCategory, TagCategoryI18N, UserProfile, Gallery, GalleryI18N, UserTag, UserEquipment
- [ x]
jhipster infooutput is mandatory for bug reports. This will allow us to use automated tests and generate the broken sample usingjhipster from-issuecommand.
Motivation for or Use Case
Visually breaks the search functionality in the admin UI when searching by a phrase and using React in the admin UI and Elasticsearch under the hood. User sees bad search results (even though the backend returns the correct data).
Reproduce the error
- Create a few entries for your entity (again, the exact model doesn't matter) - e.g. 10 entries for the
Photo- first 3 withCREATIVE_COMMONSlicense, the rest withPUBLIC_DOMAIN - The default entity list view should show the first 3 rows of
Photosas having theCREATIVE_COMMONSlicense, the remaining 7 havingPUBLIC_DOMAIN(so far so good) - Now, search for
PUBLIC_DOMAINusing the phrase search input box at the top - The first 3
Photoswill have theCREATIVE_COMMONSas the license displayed (and should havePUBLIC_DOMAIN(Note: Other properties may also be mis-rendered)
Related issues
Didn't find any related issues. Sorry if I missed them.
Suggest a Fix
The fix needs to modify the generator to (for example) use the entity id as the key attribute value, instead of using the number of the rendered row.
Sticking with the Photo example - instead of:
<tbody>
{photoList.map((photo, i) => (
<tr key={`entity-${i}`} data-cy="entityTable">
//[...]use:
<tbody>
{photoList.map((photo, i) => (
<tr key={`entity-${photo.id}`} data-cy="entityTable">
//[...]JHipster Version(s)
8.11
Browsers and Operating System
- macOS Sequoia 15.7.1 (24G231)
- tested with Arc (1.117.0), Safari (26.0.1) & Firefox (144.0)
- [x ] Tickets opened without reproduction steps or that doesn't follows the template recommendation will be closed.
- [x ] Checking this box is mandatory (this is just to show you read everything)