npm.io
21.0.1 • Published 1 month ago

@eqproject/eqp-lookup

Licence
MIT
Version
21.0.1
Deps
1
Size
230 kB
Vulns
0
Weekly
0

Table of contents

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: disableRedirectAfterSave is a convention-based property. If disableRedirectAfterSave: true is passed inside DynamicLoaderDirectiveData, the DynamicLoaderDirective will look for a public disableRedirectAfterSave property on the loaded component and set it to true. 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