@eqproject/eqp-lookup
Table of contents
- Getting started
- ControlValueAccessor integration
- Add / Edit modal workflow
- Server-side filtering
- Public methods
- API
- Models
- Use Cases
- Credits
Required
- Angular Material installed and imported
- ng-select installed and imported (v. 21.x)
Getting started
Step 1: Install eqp-lookup:
NPM
npm install --save @eqproject/eqp-lookup
Step 2: Import the EqpLookupModule:
import { EqpLookupModule } from "@eqproject/eqp-lookup";
@NgModule({
declarations: [AppComponent],
imports: [EqpLookupModule],
bootstrap: [AppComponent]
})
export class AppModule {}
Step 3: Import ng-select styling:
Style.scss
@import "~@ng-select/ng-select/themes/material.theme.css";
ControlValueAccessor integration
EqpLookupComponent implements ControlValueAccessor, which means it participates natively in Angular's reactive and template-driven form ecosystems without any adapter boilerplate.
The component supports two binding modes and they are mutually exclusive. Choose one per instance:
FormGroup / FormControl mode — pass [formGroupInput] and [formControlNameInput] together. The component writes the selected value directly into the form control and emits it via (formControlChange).
<eqp-lookup
[entityType]="'City'"
[initialItems]="cities"
[formGroupInput]="myForm"
[formControlNameInput]="'city'"
(formControlChange)="onCityChange($event)">
</eqp-lookup>
ngModel mode — pass [(ngModelInput)] for two-way binding. Changes are emitted via (ngModelInputChange).
<eqp-lookup
[entityType]="'City'"
[initialItems]="cities"
[(ngModelInput)]="selectedCity"
(ngModelInputChange)="onCityChange($event)">
</eqp-lookup>
The component also responds correctly to .disable() / .enable() calls made on the parent FormControl or FormGroup, because setDisabledState is implemented in full.
When [bindCompleteObject]="true" (the default), the emitted value is the complete LookupDTO object. When [bindCompleteObject]="false", only the value of [bindKey] (default 'ID') is emitted and written into the form control.
Add / Edit modal workflow
The lookup supports inline creation and editing of entities through [genericAddComponent]. When configured, an add button (plus icon) is injected into the ng-select container automatically. If a selection is present and [isEditable]="true", an edit button (pencil icon) is also injected.
Setting up the host component
<eqp-lookup
[entityType]="'Question'"
[fullUrlHttpCall]="fullUrlHttpCall"
[genericAddComponent]="addConfig"
[isEditable]="true"
[formGroupInput]="myForm"
[formControlNameInput]="'question'"
(formControlChange)="onQuestionChange($event)"
(lookupAddingCompleteParent)="onAddingComplete($event)">
</eqp-lookup>
import { DynamicLoaderDirectiveData } from '@eqproject/eqp-lookup';
addConfig: DynamicLoaderDirectiveData = {
componentSelector: AddQuestionComponent,
disableRedirectAfterSave: true,
idLookupEntity: null
};
Setting up the embedded component
The component loaded inside the modal (AddQuestionComponent in the example above) must inject EqpLookupService and emit lookupAddingComplete when the save operation finishes. Pass the ID of the saved entity as the payload; pass null to signal cancellation.
import { EqpLookupService } from '@eqproject/eqp-lookup';
@Component({ ... })
export class AddQuestionComponent {
disableRedirectAfterSave: boolean = false; // required property
constructor(private eqpLookupService: EqpLookupService) {}
save() {
// ... save logic ...
this.eqpLookupService.lookupAddingComplete.emit(savedEntity.ID);
}
cancel() {
this.eqpLookupService.lookupAddingComplete.emit(null);
}
}
After lookupAddingComplete emits a non-null ID, the lookup automatically closes the modal, reloads its datasource via reloadData(savedID), and pre-selects the newly saved item. If [disableReloadOnLookupAddingCompleteParent]="true" is set on the lookup, the automatic reload is skipped and only (lookupAddingCompleteParent) is emitted.
Note:
disableRedirectAfterSaveis a convention-based property. IfdisableRedirectAfterSave: trueis passed insideDynamicLoaderDirectiveData, theDynamicLoaderDirectivewill look for a publicdisableRedirectAfterSaveproperty on the loaded component and set it totrue. If the property does not exist on the component, a runtime error is thrown.
Server-side filtering
When the lookup retrieves its data via [fullUrlHttpCall], it posts a LookupConfigDTO payload to that endpoint. Two types of filter can be included in the payload.
Simple filters ([dataFilter])
[dataFilter] accepts an Array<LinqPredicateDTO>. Each LinqPredicateDTO contains a PropertyFilters array of LinqFilterDTO objects. Filters within the same LinqPredicateDTO are joined with AND; multiple LinqPredicateDTO objects are joined with OR.
<eqp-lookup
[entityType]="'City'"
[fullUrlHttpCall]="fullUrlHttpCall"
[dataFilter]="cityFilters"
[formGroupInput]="myForm"
[formControlNameInput]="'city'">
</eqp-lookup>
import { LinqPredicateDTO, LinqFilterDTO, LinqFilterType } from '@eqproject/eqp-lookup';
this.cityFilters = [
{
PropertyFilters: [
LinqFilterDTO.createFilter('FK_Province', 10, LinqFilterType.Equal)
]
}
];
Alternatively, pass the filter inline directly on the template as shown in Case 4.
Complex filters ([complexDataFilter])
[complexDataFilter] accepts an Array<ComplexLinqPredicateDTO>. Each ComplexLinqPredicateDTO wraps its own Predicates array, allowing groups of AND conditions to be combined with OR between groups. Use ComplexLinqPredicateDTO.CreateComplexPredicate() to build the structure from a matrix of LinqPredicateDTO arrays.
import { ComplexLinqPredicateDTO, LinqFilterDTO, LinqFilterType } from '@eqproject/eqp-lookup';
this.complexFilters = ComplexLinqPredicateDTO.CreateComplexPredicate([
[
{ PropertyFilters: [LinqFilterDTO.createFilter('IsActive', true, LinqFilterType.Equal)] }
],
[
{ PropertyFilters: [LinqFilterDTO.createFilter('FK_Category', 5, LinqFilterType.Equal)] }
]
]);
Custom label composition ([customConfig])
[customConfig] accepts a LookupCustomConfig object and instructs the server endpoint to build the Label property of each LookupDTO by concatenating the listed property names, and optionally to populate FullObject with the full entity.
this.customConfig = {
LabelProperties: ['Name', 'Code', 'ID'],
IncludeFullObject: true
};
When IncludeFullObject is true, the complete entity is available via item.FullObject inside custom templates and in the emitted value.
Public methods
The following methods are available on EqpLookupComponent and can be accessed via @ViewChild.
@ViewChild(EqpLookupComponent) lookup: EqpLookupComponent;
| Method | Signature | Description |
|---|---|---|
reloadData |
reloadData(selectedItemID?: any): Promise<void> |
Reloads the datasource from initialItems or via HTTP and optionally pre-selects the item whose key matches selectedItemID. |
updateInitialItems |
updateInitialItems(items: Array<LookupDTO>, selectedItemID?: any): void |
Replaces initialItems with a new array and triggers reloadData. Useful when the available options change at runtime. |
forceWriteValue |
forceWriteValue(value: any): void |
Bypasses the normal change pipeline and directly calls writeValue on the underlying NgSelectComponent. Use this when the value must be set imperatively without emitting change events. |
API
Inputs
| Input | Type | Default | Required | Description |
|---|---|---|---|---|
| [placeholder] | string |
- |
no | Text displayed as placeholder when no item is selected. If [isRequired]="true", an asterisk is appended automatically. |
| [bindLabel] | string |
'Label' |
no | Object property to use for label. |
| [bindKey] | string |
'ID' |
no | Object property to use for key. |
| [items] | Array<objects> |
- |
no | Array of objects that lookup will show. |
| [notFoundText] | string |
'Nessun risultato trovato' |
no | Custom text when filter returns empty result. |
| [genericAddComponent] | DynamicLoaderDirectiveData |
- |
no | Object containing the component class, inputParams and other configuration parameters to add or edit elements via a modal dialog. |
| [isMultiple] | boolean |
false |
no | Defines if the lookup can accept more values, managed like an object array. |
| [isSearchable] | boolean |
true |
no | Defines if it's possible to type inside lookup and search among items. |
| [isClearable] | boolean |
true |
no | Defines if the cancel button is shown. |
| [isVirtualScroll] | boolean |
false |
no | Dynamic render of the items contained in the lookup. Useful in case of huge amount of data. |
| [isReadonly] | boolean |
false |
no | Set lookup as readonly. |
| [isRequired] | boolean |
false |
no | Set lookup as required. Appends * to the placeholder text automatically. |
| [isDisabled] | boolean |
false |
no | Disable the lookup. Also respects .disable() called on the parent FormControl via ControlValueAccessor. |
| [entityType] | string |
- |
yes | Entity name to be searched on the server when the mode chosen isn't the one with initialItems. |
| [formGroupInput] | any |
- |
no | FormGroup that contains the specific FormControl of the lookup (to use if you're not using ngModelInput). |
| [formControlNameInput] | any |
- |
no | FormControl to use inside the FormGroup passed (to use if you're not using ngModelInput). |
| [ngModelInput] | any |
- |
no | ngModel binded to lookup. (to use if you're not using FormControl) |
| [isSearchWhileComposing] | boolean |
false |
no | Whether items should be filtered while composition started. |
| [bindCompleteObject] | boolean |
true |
no | Whether the binded object must be ID or the full object. |
| [appendToInput] | string |
- |
no | Input used when the lookup is contained inside a dialog or tab and there are visualization problems. Useful value: body. If omitted, the component auto-detects the context (dialog / tab / standard page) and sets the appropriate overlay target. |
| [disableReloadOnLookupAddingCompleteParent] | boolean |
false |
no | Whether data must be reloaded after adding is complete. |
| [showOptionTooltip] | boolean |
false |
no | Defines if a tooltip must be shown on option hover. |
| [sortList] | boolean |
false |
no | Items sorting based on Label. |
| [dataFilter] | Array<LinqPredicateDTO> |
- |
no | It allows you to pass some simple filters to the server (used when fullUrlHttpCall is also passed) |
| [customConfig] | LookupCustomConfig |
- |
no | Configuration related to properties that compose the label and to full object inclusion (used when fullUrlHttpCall is also passed). |
| [complexDataFilter] | Array<ComplexLinqPredicateDTO> |
- |
no | It allows you to pass some complex filters to the server (used when fullUrlHttpCall is also passed) |
| [initialItems] | Array<any> |
false |
no | Initial datasource used if you want to bypass the server call. |
| [ngModelOptions] | any |
- |
no | Options related to ngModel. |
| [isEditable] | boolean |
false |
no | Defines if the selected item can be edited. Requires [genericAddComponent] to be configured. |
| [isMultiline] | boolean |
false |
no | Defines if the item selected's label can be shown inside a multiple line input. |
| [dropdownPosition] | auto | top | bottom |
auto |
no | Specifies the position of the dropdown. |
| [selectOnTab] | boolean |
false |
no | Select marked dropdown item using tab. |
| [fullUrlHttpCall] | string |
- |
no | Full HTTP url for the server call. |
| [addButtonText] | string |
'Crea nuovo' |
no | Text displayed as tooltip when hovering on the add button. |
| [editButtonText] | string |
'Modifica' |
no | Text displayed as tooltip when hovering on the edit button. |
| [selectAll] | boolean |
false |
no | Add an option inside the dropdown that allows the user to select all the elements. |
| [selectAllText] | string |
'Seleziona tutto' |
no | Text displayed in the select all option. |
| [groupBy] | string | Function |
- |
no | Allow to group items by key or function expression. |
| [groupValue] | (groupKey: string, children: any[]) => Object |
- |
no | Function expression to provide group value. |
| [groupByProperty] | string |
- |
no | Property name used to group items. Required when [selectableGroupAsModel]="true" so that group selection can be exploded correctly. |
| [selectableGroup] | boolean |
false |
no | Allow to select group when groupBy is used. |
| [selectableGroupAsModel] | boolean |
false |
no | Indicates whether to select all children or group itself. When true, group selection is automatically "exploded" into its individual child items before being emitted via the change outputs. |
| [clearAllText] | string |
'Elimina' |
no | Text displayed as tooltip when hovering the clear button. |
| [customOption] | TemplateRef |
- |
no | Custom template for the option inside the dropdown panel. |
| [customLabel] | TemplateRef |
- |
no | Custom template for the selected item's label. |
| [manipulateDataFn] | (items: any) => LookupDTO[] |
- |
no | Function to manipulate data after the HTTP call used to retrieve items. |
| [compareFunction] | (iteratedObject: any, bindedObject: any) => boolean |
- |
no | Function to compare the option values with the selected values. A boolean should be returned. By default the component uses a smart comparison that handles both primitive values and objects by comparing the Key or bindKey property. |
| [selectFirstItem] | boolean |
false |
no | Property that allows to select the first item of the lookup if there is not already a selection. |
| [selectIfSingleItem] | boolean |
false |
no | Property that allows to select the only item of the lookup if there is only one item and there is not already a selection. |
| [initialSelectedID] | any |
- |
no | ID (or a value of the BindKey selected) that allows to select the corresponding item of the lookup during the Init of the component. |
| [appearance] | string |
'' |
no | Defines the Angular Material form field appearance. Accepted values are those defined by Angular Material: 'outline', 'fill'. When empty the default Material theme from ng-select is used. |
Outputs
| Output | Event arguments | Required | Description |
|---|---|---|---|
| (ngModelInputChange) | any |
- |
Invoked when the value changes and the ngModelInput is binded. When items are grouped it return the group 'exploded'. |
| (formControlChange) | any |
- |
Invoked when the value changes and a form control is binded. When items are grouped it return the group 'exploded'. |
| (lookupAddingCompleteParent) | any |
- |
Event emitted when the add process is completed and disableReloadOnLookupAddingCompleteParent is not true. |
| (searchChange) | any |
- |
Invoked when user searches among lookup items. It return the term typed and items filtered. |
| (keydown) | any |
- |
Invoked on keydown. |
| (clear) | any |
- |
Event emitted on select clear. |
| (valueSelected) | LookupDTO |
- |
Event emitted when a value is selected. |
| (itemsUpdated) | LookupDTO[] |
- |
Event emitted when the list of items of the lookup is updated (after every reloadData call). |
Models
LookupDTO
The standard data transfer object returned by the server endpoint and used internally throughout the component.
| Property | Type | Description |
|---|---|---|
| ID | number | string | null |
Unique identifier of the item. Corresponds to the [bindKey] property (default 'ID'). |
| Label | string | null |
Display text of the item. Corresponds to the [bindLabel] property (default 'Label'). |
| FullObject | any |
Full entity object, populated when LookupCustomConfig.IncludeFullObject is true. |
LookupCustomConfig
Passed via [customConfig] to control how the server builds each LookupDTO.Label and whether the full entity is included.
| Property | Type | Description |
|---|---|---|
| LabelProperties | Array<string> |
List of entity property names whose values are concatenated (in order) to form the Label of each LookupDTO. |
| IncludeFullObject | boolean |
If true, the server populates LookupDTO.FullObject with the complete entity. |
DynamicLoaderDirectiveData
Passed via [genericAddComponent] to configure the inline add/edit modal.
| Property | Type | Default | Description |
|---|---|---|---|
| componentSelector | any |
- | The Angular component class to dynamically load inside the modal. |
| inputParams | any |
null |
Optional object whose keys are mapped to properties of the loaded component instance. |
| idLookupEntity | number | null |
null |
ID of the entity to edit. Pass null or 0 for creation mode. The lookup updates this value automatically after a successful save. |
| isEditable | boolean |
false |
N.D. |
| disableRedirectAfterSave | boolean |
undefined |
If true, sets disableRedirectAfterSave = true on the loaded component. The component must expose this property or a runtime error is thrown. |
LinqPredicateDTO / LinqFilterDTO / LinqFilterType
Used with [dataFilter] to build server-side filter conditions.
import { LinqPredicateDTO, LinqFilterDTO, LinqFilterType } from '@eqproject/eqp-lookup';
// Shorthand factory method
const filter = LinqFilterDTO.createFilter('IsActive', true, LinqFilterType.Equal);
const predicate: LinqPredicateDTO = { PropertyFilters: [filter] };
LinqFilterType enum values:
| Value | Name | Description |
|---|---|---|
| 1 | Equal | Property equals value |
| 2 | NotEqual | Property does not equal value |
| 3 | StringContains | String property contains value |
| 4 | StringNotContains | String property does not contain value |
| 5 | GreaterThan | Property is greater than value |
| 6 | GreaterThanOrEqual | Property is greater than or equal to value |
| 7 | LessThan | Property is less than value |
| 8 | LessThanOrEqual | Property is less than or equal to value |
| 9 | ContainsElement | Collection property contains element |
| 10 | NotContainsElement | Collection property does not contain element |
LinqFilterDTO properties:
| Property | Type | Description |
|---|---|---|
| PropertyName | string |
Name of the entity property to filter on. |
| PropertyValue | any |
Value to compare against. |
| RelationType | LinqFilterType |
Comparison operator. |
| ListElementPropertyName | string | null |
Optional. Used when filtering on a property of an element inside a collection property. |
ComplexLinqPredicateDTO
Used with [complexDataFilter] to build groups of AND conditions joined by OR.
import { ComplexLinqPredicateDTO, LinqFilterDTO, LinqFilterType } from '@eqproject/eqp-lookup';
const complexFilters = ComplexLinqPredicateDTO.CreateComplexPredicate([
// Group 1 (AND between filters)
[{ PropertyFilters: [LinqFilterDTO.createFilter('IsActive', true, LinqFilterType.Equal)] }],
// Group 2 (OR with Group 1)
[{ PropertyFilters: [LinqFilterDTO.createFilter('FK_Category', 5, LinqFilterType.Equal)] }]
]);
Use cases
Case 1: Lookup with initial items :
<eqp-lookup
placeholder="Lookup with initial items"
[entityType]="'Question'"
[initialItems]="questionInitialItems"
[formGroupInput]="lookupFormGroup"
[formControlNameInput]="'lookupWithInitialItems'"
(formControlChange)="lookupChange($event)"></eqp-lookup>
Define properties and functions in Typescript (or Javascript) file
lookupFormGroup : FormGroup | undefined;
...
createForm() {
this.lookupFormGroup = this.formBuilder.group({
lookupWithInitialItems: [null, Validators.required],
})
}
...
this.http.post<LookupDTO[]>(url, config).subscribe(res => {
this.questionInitialItems = res;
})
...
lookupChange(event: any) {
// body
}
Case 2: Lookup with HTTP call and ngModelInput :
Define component selector in HTML
<eqp-lookup
placeholder="Lookup with HTTP call"
[entityType]="'Question'"
[fullUrlHttpCall]="fullUrlHttpCall"
[(ngModelInput)]="lookupWithHttpCall"
(ngModelInputChange)="lookupChange($event)"
[isMultiline]="true"></eqp-lookup>
Define properties and functions in Typescript (or Javascript) file
fullUrlHttpCall = environment.apiFullUrl + '/lookup/GetLookupEntities';
lookupWithHttpCall: LookupDTO | undefined;
...
lookupChange(event: any) {
// body
}
Case 3: Lookup with HTTP call and Form :
Define component selector in HTML
<eqp-lookup
placeholder="Lookup with aform"
[entityType]="'Question'"
[fullUrlHttpCall]="fullUrlHttpCall"
[formGroupInput]="lookupFormGroup"
[formControlNameInput]="'lookupWithForm'"
(formControlChange)="lookupChange($event)"
[isMultiline]="true"></eqp-lookup>
Define properties and functions in Typescript (or Javascript) file
fullUrlHttpCall = environment.apiFullUrl + '/lookup/GetLookupEntities';
lookupFormGroup : FormGroup | undefined;
...
createForm() {
this.lookupFormGroup = this.formBuilder.group({
lookupWithForm: [null, Validators.required],
})
}
...
lookupChange(event: any) {
// body
}
Case 4: Lookup with HTTP call and extra configs :
Define component selector in HTML
<eqp-lookup
[entityType]="'City'"
placeholder="Città"
[fullUrlHttpCall]="fullUrlHttpCall"
[formGroupInput]="lookupFormGroup"
[formControlNameInput]="'lookupWithExtraConfigs'"
(formControlChange)="lookupChange($event)"
[dataFilter]="[{PropertyFilters: [{PropertyName: 'FK_Province', PropertyValue: 10, RelationType: 1}]}]"
[customConfig]="{LabelProperties: ['Name', 'Code', 'ID'], IncludeFullObject: true}"></eqp-lookup>
Define properties and functions in Typescript (or Javascript) file
fullUrlHttpCall = environment.apiFullUrl + '/lookup/GetLookupEntities';
lookupFormGroup : FormGroup | undefined;
...
createForm() {
this.lookupFormGroup = this.formBuilder.group({
lookupWithExtraConfigs: [null, Validators.required],
})
}
...
lookupChange(event: any) {
// body
}
Case 5: Editable Lookup :
Define component selector in HTML
<eqp-lookup
placeholder="Editable lookup"
[entityType]="'Question'"
[fullUrlHttpCall]="fullUrlHttpCall"
[genericAddComponent]="{componentSelector: addQuestionComponent, idLookupEntity: getNumber(getFormControls('editableLookup').value), isEditable: true, disableRedirectAfterSave: true}"
[isVirtualScroll]="true"
[formGroupInput]="lookupFormGroup"
[formControlNameInput]="'editableLookup'"
(formControlChange)="lookupChange($event)"
[isMultiline]="true"
[isEditable]="true"></eqp-lookup>
Define properties and functions in Typescript (or Javascript) file
fullUrlHttpCall = environment.apiFullUrl + '/lookup/GetLookupEntities';
lookupFormGroup : FormGroup | undefined;
addQuestionComponent = AddQuestionComponent;
...
createForm() {
this.lookupFormGroup = this.formBuilder.group({
editableLookup: [null, Validators.required],
})
}
...
getNumber(element: number | string | undefined | null): number | null {
if (element != null)
return Number(element);
else
return <null><unknown> element;
}
...
getFormControls(control: string) {
return this.lookupFormGroup.controls[control];
}
...
lookupChange(event: any) {
// body
}
Case 6: Lookup with select all :
Define component selector in HTML
<eqp-lookup
placeholder="Lookup with select all"
[entityType]="'Question'"
[fullUrlHttpCall]="fullUrlHttpCall"
[isVirtualScroll]="true"
(formControlChange)="lookupChange($event)"
[formGroupInput]="lookupFormGroup"
[formControlNameInput]="'lookupWithSelectAll'"
[isMultiline]="true"
[isEditable]="true"
[selectAll]="true"
[isMultiple]="true"
[selectableGroupAsModel]="true"></eqp-lookup>
Define properties and functions in Typescript (or Javascript) file
fullUrlHttpCall = environment.apiFullUrl + '/lookup/GetLookupEntities';
lookupFormGroup : FormGroup | undefined;
...
createForm() {
this.lookupFormGroup = this.formBuilder.group({
lookupWithSelectAll: [null, Validators.required],
})
}
...
lookupChange(event: any) {
// body
}
Case 7: Lookup with groups :
Define component selector in HTML
<eqp-lookup
placeholder="Lookup with groups"
[entityType]="'Question'"
[fullUrlHttpCall]="fullUrlHttpCall"
[isVirtualScroll]="true"
[formGroupInput]="lookupFormGroup"
[formControlNameInput]="'lookupWithGroups'"
(formControlChange)="lookupChange($event)"
[isMultiline]="true"
[groupBy]="groupByFn"
[groupValue]="groupValueFn"
[groupByProperty]="'FullObject.FK_QuestionCategory'"
[isMultiple]="true"
[selectableGroup]="true"
[selectableGroupAsModel]="true"></eqp-lookup>
Define properties and functions in Typescript (or Javascript) file
fullUrlHttpCall = environment.apiFullUrl + '/lookup/GetLookupEntities';
lookupFormGroup : FormGroup | undefined;
groupByFn = (item: any) => item.FullObject.FK_QuestionCategory
groupValueFn = (groupKey: string, children: any[]) => ({Key: groupKey, Label: groupKey, Total: children.length});
...
createForm() {
this.lookupFormGroup = this.formBuilder.group({
lookupWithGroups: [null, Validators.required],
})
}
...
lookupChange(event: any) {
// body
}
NB: It's possible to change the Label displayed for groups passing some values or using the groupKey to access the selected properties inside an array.
Case 8: Lookup with data manipulation and custom templates :
Define component selector in HTML
<eqp-lookup
placeholder="Lookup with function and custom templates"
[entityType]="'Question'"
[fullUrlHttpCall]="fullUrlHttpCall"
[formGroupInput]="lookupFormGroup"
[formControlNameInput]="'lookupWithFunctionAndCustomTemplates'"
(formControlChange)="lookupChange($event)"
[isMultiline]="true"
[selectAll]="true"
[customOption]="customOptionTemplate"
[customLabel]="customLabelTemplate"
[manipulateDataFn]="manipulateData"></eqp-lookup>
Define custom templates in HTML
<!-- Template for option inside the dropdown panel -->
<ng-template #customOptionTemplate ng-option-tmp let-item="item" let-index="index" let-search="searchTerm">
<div class="card">
<div class="card-body">
<h5 class="card-title">{{item.Label}}</h5>
<h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
<p class="card-text">Some quick example text to build</p>
<div>
<a href="#" class="card-link">Card link</a>
<a href="#" class="card-link">Another link</a>
</div>
</div>
</div>
</ng-template>
<!-- Selected items' Label template -->
<ng-template #customLabelTemplate ng-label-tmp let-item="item" let-clear="clear">
<span title="{{item.Label}}">Custom template: {{item.Label}}</span>
<span *ngIf="item.Total" class="ml-1 badge badge-secondary" style="color: black !important">{{item.Total}}</span>
<span class="ng-value-icon right" (click)="clear(item)" aria-hidden="true"> x </span>
</ng-template>
Define properties and functions in Typescript (or Javascript) file
fullUrlHttpCall = environment.apiFullUrl + '/lookup/GetLookupEntities';
lookupFormGroup : FormGroup | undefined;
@ViewChild("customOptionTemplate") customOptionTemplate: TemplateRef<any>;
@ViewChild("customLabelTemplate") customLabelTemplate: TemplateRef<any>;
...
createForm() {
this.lookupFormGroup = this.formBuilder.group({
lookupWithFunctionAndCustomTemplates: [null, Validators.required],
})
}
manipulateData(items: any) : LookupDTO[] {
let res: LookupDTO[] = [];
for(let item of items) {
item.Label += " added later";
res.push(item)
}
return res;
}
...
lookupChange(event: any) {
// body
}
Case 9: Lookup with runtime datasource update :
Use updateInitialItems to replace the datasource at runtime without having to re-configure the component. This is useful when the available options depend on the selection made in another field.
<eqp-lookup
#cityLookup
placeholder="City"
[entityType]="'City'"
[initialItems]="cities"
[formGroupInput]="myForm"
[formControlNameInput]="'city'"
(formControlChange)="onCityChange($event)">
</eqp-lookup>
@ViewChild('cityLookup') cityLookup: EqpLookupComponent;
onProvinceChange(province: any) {
// Filter cities based on selected province
const filtered = this.allCities.filter(c => c.FK_Province === province.ID);
// Replace the datasource and reset the selection
this.cityLookup.updateInitialItems(filtered);
}
Case 10: Lookup with complex server-side filters :
Use [complexDataFilter] when the server filter requires groups of AND conditions joined by OR.
<eqp-lookup
placeholder="Prodotto"
[entityType]="'Product'"
[fullUrlHttpCall]="fullUrlHttpCall"
[complexDataFilter]="productFilters"
[formGroupInput]="myForm"
[formControlNameInput]="'product'"
(formControlChange)="onProductChange($event)">
</eqp-lookup>
import { ComplexLinqPredicateDTO, LinqFilterDTO, LinqFilterType } from '@eqproject/eqp-lookup';
// Selects products that are either active OR belong to category 5
this.productFilters = ComplexLinqPredicateDTO.CreateComplexPredicate([
[{ PropertyFilters: [LinqFilterDTO.createFilter('IsActive', true, LinqFilterType.Equal)] }],
[{ PropertyFilters: [LinqFilterDTO.createFilter('FK_Category', 5, LinqFilterType.Equal)] }]
]);
Credits
This library has been developed by EqProject SRL, for more info contact: info@eqproject.it