Skip to content

Commit 3a34aa6

Browse files
committed
Add connection lifecycle events to typeahead data sources
Introduced `onConnected` and `onDisconnected` observables to manage connection lifecycle events for `TypeaheadDataSource` and related classes. Updated constructors, methods, and dependencies to handle these events, ensuring better lifecycle management. Also reintroduced `ngDoCheck` to refine error state updates. Updated package versions to reflect changes.
1 parent 4eef37f commit 3a34aa6

5 files changed

Lines changed: 81 additions & 29 deletions

File tree

projects/merelis/angular-material/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@merelis/angular-material",
3-
"version": "1.0.2",
3+
"version": "1.3.3",
44
"license": "MIT",
55
"author": "Jean Merelis",
66
"repository": {

projects/merelis/angular-material/select/src/select-form-control.ts

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -165,21 +165,21 @@ export class MerSelectFormFieldControl<T> implements MatFormFieldControl<T>, OnI
165165
}
166166
}
167167

168-
// ngDoCheck() {
169-
// if (this.ngControl) {
170-
// // We need to re-evaluate this on every change detection cycle, because there are some
171-
// // error triggers that we can't subscribe to (e.g. parent form submissions). This means
172-
// // that whatever logic is in here has to be super lean or we risk destroying the performance.
173-
// this.updateErrorState();
174-
//
175-
// }
176-
//
177-
// // We need to dirty-check and set the placeholder attribute ourselves, because whether it's
178-
// // present or not depends on a query which is prone to "changed after checked" errors.
179-
// this._dirtyCheckPlaceholder();
180-
// }
181-
//
182-
//
168+
ngDoCheck() {
169+
if (this.ngControl) {
170+
// We need to re-evaluate this on every change detection cycle, because there are some
171+
// error triggers that we can't subscribe to (e.g. parent form submissions). This means
172+
// that whatever logic is in here has to be super lean or we risk destroying the performance.
173+
this.updateErrorState();
174+
175+
}
176+
177+
// We need to dirty-check and set the placeholder attribute ourselves, because whether it's
178+
// present or not depends on a query which is prone to "changed after checked" errors.
179+
// this._dirtyCheckPlaceholder();
180+
}
181+
182+
183183
// private _previousPlaceholder?: string | null;
184184
// /** Does some manual dirty checking on the native input `placeholder` attribute. */
185185
// private _dirtyCheckPlaceholder() {
@@ -192,7 +192,7 @@ export class MerSelectFormFieldControl<T> implements MatFormFieldControl<T>, OnI
192192
// // : element.removeAttribute('placeholder');
193193
// }
194194
// }
195-
//
195+
196196
// /** Gets the current placeholder of the form field. */
197197
// protected _getPlaceholder(): string | null {
198198
// return this.placeholder || null;

projects/merelis/angular/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@merelis/angular",
3-
"version": "1.3.2",
3+
"version": "1.3.3",
44
"license": "MIT",
55
"author": "Jean Merelis",
66
"repository": {

projects/merelis/angular/select/select/select.datasource.ts

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Comparable, DisplayWith, FilterPredicate } from "../types";
2-
import { BehaviorSubject, Observable } from "rxjs";
2+
import { BehaviorSubject, Observable, Subject } from "rxjs";
33

44
export interface FilterCriteria<T> {
55
searchText?: string;
@@ -37,6 +37,8 @@ export class MerSelectDataSource<T> implements SelectDataSource<T> {
3737
protected _filteredData = new BehaviorSubject<T[]>([]);
3838
protected _currentFilter = new BehaviorSubject<FilterCriteria<T>>({});
3939
protected _alwaysIncludesSelected = false;
40+
protected _onConnected = new Subject<void>();
41+
protected _onDisconnected = new Subject<void>();
4042

4143
get data(): T[] {
4244
return this._data.value;
@@ -74,6 +76,14 @@ export class MerSelectDataSource<T> implements SelectDataSource<T> {
7476
this._applyFilters();
7577
}
7678

79+
get onConnected(): Observable<void> {
80+
return this._onConnected.asObservable();
81+
}
82+
83+
get onDisconnected(): Observable<void> {
84+
return this._onDisconnected.asObservable();
85+
}
86+
7787
constructor(data?: T[], options?: {
7888
alwaysIncludesSelected?: boolean,
7989
compareWith?: Comparable<T>,
@@ -100,17 +110,20 @@ export class MerSelectDataSource<T> implements SelectDataSource<T> {
100110
}
101111

102112
connect(): Observable<T[]> {
113+
this._onConnected.next();
103114
return this._filteredData.asObservable();
104115
}
105116

106117
disconnect(): void {
107-
// No need to track viewers anymore
108-
}
109-
110-
dispose(): void {
111118
this._data.complete();
112119
this._filteredData.complete();
113120
this._currentFilter.complete();
121+
this._onConnected.complete();
122+
this._onDisconnected.complete();
123+
this._onConnected.complete();
124+
125+
this._onDisconnected.next();
126+
this._onDisconnected.complete();
114127
}
115128

116129
loading(): Observable<boolean> | undefined {

projects/merelis/angular/select/select/typeadhead.datasource.ts

Lines changed: 45 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ export interface TypeaheadSearchService<T> {
1414
search(query: string): Observable<T[]>;
1515
}
1616

17+
export type TypeaheadSearchFn<T> = (query: string) => Observable<T[]>;
18+
1719
export interface TypeaheadDataSourceOptions<T> {
1820

1921
/**
@@ -43,6 +45,9 @@ export class TypeaheadDataSource<T> implements SelectDataSource<T> {
4345
// Stream of current data
4446
private data = new BehaviorSubject<T[]>([]);
4547

48+
protected _onConnected = new Subject<void>();
49+
protected _onDisconnected = new Subject<void>();
50+
4651
// Stream of loading state
4752
private loading$ = new BehaviorSubject<boolean>(false);
4853

@@ -54,12 +59,36 @@ export class TypeaheadDataSource<T> implements SelectDataSource<T> {
5459
private compareWith: (a: T, b: T) => boolean;
5560

5661
/**
57-
* Creates a new TypeaheadDataSource
58-
* @param searchService Service that implements the search functionality
59-
* @param options
62+
* Returns an Observable that emits when the connection is established.
63+
*
64+
* @return {Observable<void>} An Observable that emits a void value when the connection occurs.
65+
*/
66+
get onConnected(): Observable<void> {
67+
return this._onConnected.asObservable();
68+
}
69+
70+
/**
71+
* Returns an observable that emits when the disconnection event occurs.
72+
*
73+
* @return {Observable<void>} An observable that emits a void value upon disconnection.
74+
*/
75+
get onDisconnected(): Observable<void> {
76+
return this._onDisconnected.asObservable();
77+
}
78+
79+
/**
80+
* Constructs an instance of the class with the provided search service and options.
81+
* Initializes the typeahead search pipeline, including handling cancellation of previous search requests, managing the inclusion of selected items, and emitting search results.
82+
*
83+
* @param {TypeaheadSearchFn<T> | TypeaheadSearchService<T>} searchService - The service or function used to perform the typeahead search. If a function is provided, it should return an observable with the search results.
84+
* @param {TypeaheadDataSourceOptions<T>} [options={}] - Optional configuration options for the typeahead data source. Includes settings such as whether to always include selected items or suppress loading events.
85+
* - `alwaysIncludeSelected`: Whether to always include selected items in the search results.
86+
* - `suppressLoadingEvents`: Whether to suppress loading indicators during search operations.
87+
* - `compareWith`: A comparison function to determine equality between items.
88+
*
6089
*/
6190
constructor(
62-
private searchService: TypeaheadSearchService<T>,
91+
private searchService: TypeaheadSearchFn<T> | TypeaheadSearchService<T>,
6392
private options: TypeaheadDataSourceOptions<T> = {}
6493
) {
6594
this.alwaysIncludeSelected = options.alwaysIncludeSelected ?? false;
@@ -75,7 +104,12 @@ export class TypeaheadDataSource<T> implements SelectDataSource<T> {
75104
this.loading$.next(true);
76105
}
77106

78-
return this.searchService.search(criteria.searchText || '').pipe(
107+
const searchQuery = criteria.searchText || '';
108+
const searchResult = typeof this.searchService === 'function'
109+
? this.searchService(searchQuery)
110+
: this.searchService.search(searchQuery);
111+
112+
return searchResult.pipe(
79113
// Process the results to include selected items if needed
80114
map(results => {
81115
// Only process if we have search text, selected items, and alwaysIncludeSelected is true
@@ -137,6 +171,7 @@ export class TypeaheadDataSource<T> implements SelectDataSource<T> {
137171
* Required by SelectDataSource interface - returns the data stream
138172
*/
139173
connect(): Observable<T[]> {
174+
this._onConnected.next();
140175
return this.data.asObservable();
141176
}
142177

@@ -147,6 +182,10 @@ export class TypeaheadDataSource<T> implements SelectDataSource<T> {
147182
this.searchSubject.complete();
148183
this.loading$.complete();
149184
this.data.complete();
185+
this._onConnected.complete();
186+
187+
this._onDisconnected.next();
188+
this._onDisconnected.complete();
150189
}
151190

152191
/**
@@ -160,7 +199,7 @@ export class TypeaheadDataSource<T> implements SelectDataSource<T> {
160199
* Main method used by MerSelect for filtering
161200
* @param criteria The filter criteria from the component
162201
*/
163-
async applyFilter(criteria: FilterCriteria<T>): Promise<void> {
202+
applyFilter(criteria: FilterCriteria<T>): void {
164203
// Pass the entire criteria object to our subject
165204
this.searchSubject.next(criteria);
166205
}

0 commit comments

Comments
 (0)