SpringBot Customising the Dropdown Component Contents
Model

For this article we will be using our Fishnatics entity model as our example, specifically the Tank
entity.
From this example, we can see the two model-driven methods for populating our dropdown component.
Enumerated Types
These are controlled by the Enum
attribute type.
These would be selected for the use case where the choices do not change often.

References
These are controlled by the references between entities.
These would be selected when the choices change or a binding between entities needs to be made.

Customisation
There are many different use cases for customising the content of a dropdown.
We will go through a couple of the primary ones in this article.
Filtering on a Reference
Sometimes, you want to only show references in a dropdown which match a condition.
For example, we may only want to be able to add fish that are Alive
to a Tank
.
To achieve this we will filter on the on the reference with our Fish
entity and select for those that have alive=true
.

Abstract Model
We will first look at our abstract model found at clientside/src/app/lib/models/abstract.model.ts
export interface ModelRelation {
// Name of the Relation to bind with, usually is the id
name: string;
// Label to display in the input component, usually the Name or OppositeName of the references
label: string;
// The field of the model to display in the component
displayName: string;
type: ModelRelationType;
// Validators to be used in the form
validators?: ValidatorFn[];
// Function for the dropdown to apply the server side searching
searchFunction?: Subject<string>;
// Observable to do the server side fetching
collection?: Observable<any[]>;
// Collection state of one instance of relations. Used in searching
stateConfig?: PassableStateConfig<any>;
group?: { id: string, displayName: string };
// Whether the data is sensitive, which could not be fetched from db, and would be show as password fields
isSensitive?: boolean;
// Used to control whether to hide element by from the view
hideElement?: boolean;
index?: number;
[s: string]: any;
// % protected region % [Add any additional model relation fields here] off begin
// % protected region % [Add any additional model relation fields here] end
}
For this task, we will be using the searchFunction
property.
Given that this makes use of the server-side search, we first must make sure that our alive
attribute on our Fish
entity is searchable.
Make sure you rebuild your app once you have made this change.
Looking at the FishService.java
found at serverside/src/main/java/com/springbot/fishnatics/services/FishService.java
, we can see where this change has occurred in the processCondition(Where condition)
method. You will notice there is now a condition for our alive
attribute.
Adding the Reference Search Criteria
-
First we we need to set how the predicate for
alive
is defined.Turn on the protected region called
Add any additional logic after the query parameters of entity properties here
and add the following:// % protected region % [Add any additional logic after the query parameters of entity properties here] on begin switch (condition.getOperation()) { case equal: predicate = entity.alive.eq(Boolean.valueOf(condition.getValue())); break; case notEqual: predicate = entity.alive.ne(Boolean.valueOf(condition.getValue())); break; default: // Allow to fall through } // % protected region % [Add any additional logic after the query parameters of entity properties here] end
- Now, we need to override the default reference collection handling within the CRUD tile. Open
clientside/src/app/admin/tiles/crud/tank/tank.admin.tile.crud.component.ts
and activate the protected regionAdd any additional class methods here
. -
Add the following:
// % protected region % [Add any additional class methods here] on begin private customPrepareReferenceCollections() { this.modelRelations.fishs.stateConfig = { pageIndex: 0, pageSize: this.pageSize, collectionId: this.collectionId, queryParams: { pageSize: this.pageSize, pageIndex: 0, where: [ [ { path: "alive", operation: QueryOperation.EQUAL, value: "true" } ] ] } } as PassableStateConfig<FishModel>; this.store.dispatch(new fishModelAction.InitialiseFishCollectionState(this.modelRelations.fishs.stateConfig)); this.modelRelations.fishs.collection = this.store.select(getFishCollectionModels, this.collectionId); this.customAddFishSearchFunction(this.modelRelations.fishs, fishModelAction.FetchFishModelsWithQuery); this.store.dispatch(new fishModelAction.FetchFishModelsWithQuery(this.modelRelations.fishs.stateConfig)); } private customAddFishSearchFunction(modelRelation: ModelRelation, action: new (...args: any[]) => NgRxAction) { modelRelation.searchFunction = new Subject<string>(); modelRelation.searchFunction.pipe( debounceTime(500), distinctUntilChanged() ).subscribe( (term: string) => { modelRelation.stateConfig.queryParams = { pageSize: this.pageSize, pageIndex: 0, where: [ [ { path: "alive", operation: QueryOperation.EQUAL, value: "true" }, { path: modelRelation.displayName, operation: QueryOperation.CONTAINS, value: term } ] ] }; this.store.dispatch(new action(modelRelation.stateConfig)); } ); } // % protected region % [Add any additional class methods here] end
The key changes we have made are,
- Added query parameters to use our new filter for both initialise and search
- Replace our fetch all with a fetch with query so that our filter is applied
-
To deactivate the original
prepareReferenceCollections()
method, we need to activate the protected region calledAdd any additional code here before the main logic of prepareReferenceCollections
and add the following:// % protected region % [Add any additional code here before the main logic of prepareReferenceCollections] on begin return; // % protected region % [Add any additional code here before the main logic of prepareReferenceCollections] end
Note: This does turn the old method into dead code, this will be rectified in future versions.
-
Final step is to execute our new
customPrepareReferenceCollections()
method. Activate the protected regions calledAdd additional processing for state configuration here
andAdd additional processing for Create mode before the main body here
and place the following inside both:this.customPrepareReferenceCollections();
This will enable our new method to replace the old for both create and update.
What you will now see is that the Fish
entity dropdown is will now only show Fish
that are alive
.
Transformation of Dropdown Data
Perhaps the data does not require filtering but rather transformation, for example, adding internationalisation support.
This can easily be achieved globally for a given attribute by overriding our getter methods.
Following on from the previous example we will transform our Fish Entity.

- First thing to take note of is our
displayName
for our fish reference. Openclientside/src/app/models/tank/tank.model.ts
and locate the method calledgetRelations()
. You will notice ourfishs
object. This has a property calleddisplayName
which references an attribute on theFishModel
(found atfish.model.ts
). This is the value we will wish to transform in this example. - Next we want to override our getter method for our display attribute. Open the
FishEntity.java
file fromserverside/src/main/java/com/springbot/basic/entities/FishEntity.java
, find the protected regionAdd any additional class methods here
and activate it. -
Add the following:
// % protected region % [Add any additional class methods here] on begin public String getName() { return this.name.toUpperCase(); } public void setName(String name) { this.name = name.toLowerCase(); } // % protected region % [Add any additional class methods here] end
This will perform the simple text transform of converting the name to upper case every time we fetch it, and converting it back to lowercase every time we save it back.
There are many ways of achieving a solution for this use case, including:
- Adding transient variables to the entity to avoid persistence
- Adding the transformation to the service for the specific request required
This article only explores the simple case.
Was this article helpful?