@smallpearl/ngx-helper 0.29.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +230 -0
- package/core/index.d.ts +2 -0
- package/core/src/ngx-helper.d.ts +7 -0
- package/core/src/version.d.ts +1 -0
- package/entity-field/index.d.ts +2 -0
- package/entity-field/src/entity-field-spec.d.ts +69 -0
- package/entity-field/src/provider.d.ts +27 -0
- package/fesm2022/smallpearl-ngx-helper-core.mjs +23 -0
- package/fesm2022/smallpearl-ngx-helper-core.mjs.map +1 -0
- package/fesm2022/smallpearl-ngx-helper-entity-field.mjs +112 -0
- package/fesm2022/smallpearl-ngx-helper-entity-field.mjs.map +1 -0
- package/fesm2022/smallpearl-ngx-helper-forms.mjs +112 -0
- package/fesm2022/smallpearl-ngx-helper-forms.mjs.map +1 -0
- package/fesm2022/smallpearl-ngx-helper-hover-dropdown.mjs +108 -0
- package/fesm2022/smallpearl-ngx-helper-hover-dropdown.mjs.map +1 -0
- package/fesm2022/smallpearl-ngx-helper-locale.mjs +296 -0
- package/fesm2022/smallpearl-ngx-helper-locale.mjs.map +1 -0
- package/fesm2022/smallpearl-ngx-helper-mat-busy-wheel.mjs +504 -0
- package/fesm2022/smallpearl-ngx-helper-mat-busy-wheel.mjs.map +1 -0
- package/fesm2022/smallpearl-ngx-helper-mat-context-menu.mjs +184 -0
- package/fesm2022/smallpearl-ngx-helper-mat-context-menu.mjs.map +1 -0
- package/fesm2022/smallpearl-ngx-helper-mat-entity-crud.mjs +1486 -0
- package/fesm2022/smallpearl-ngx-helper-mat-entity-crud.mjs.map +1 -0
- package/fesm2022/smallpearl-ngx-helper-mat-entity-list.mjs +800 -0
- package/fesm2022/smallpearl-ngx-helper-mat-entity-list.mjs.map +1 -0
- package/fesm2022/smallpearl-ngx-helper-mat-file-input.mjs +328 -0
- package/fesm2022/smallpearl-ngx-helper-mat-file-input.mjs.map +1 -0
- package/fesm2022/smallpearl-ngx-helper-mat-form-error.mjs +468 -0
- package/fesm2022/smallpearl-ngx-helper-mat-form-error.mjs.map +1 -0
- package/fesm2022/smallpearl-ngx-helper-mat-select-entity.mjs +854 -0
- package/fesm2022/smallpearl-ngx-helper-mat-select-entity.mjs.map +1 -0
- package/fesm2022/smallpearl-ngx-helper-mat-side-menu-layout.mjs +930 -0
- package/fesm2022/smallpearl-ngx-helper-mat-side-menu-layout.mjs.map +1 -0
- package/fesm2022/smallpearl-ngx-helper-mat-tel-input.mjs +926 -0
- package/fesm2022/smallpearl-ngx-helper-mat-tel-input.mjs.map +1 -0
- package/fesm2022/smallpearl-ngx-helper-sideload.mjs +111 -0
- package/fesm2022/smallpearl-ngx-helper-sideload.mjs.map +1 -0
- package/fesm2022/smallpearl-ngx-helper-stationary-with-line-items.mjs +384 -0
- package/fesm2022/smallpearl-ngx-helper-stationary-with-line-items.mjs.map +1 -0
- package/fesm2022/smallpearl-ngx-helper.mjs +13 -0
- package/fesm2022/smallpearl-ngx-helper.mjs.map +1 -0
- package/forms/index.d.ts +1 -0
- package/forms/src/validation-error-handler.d.ts +52 -0
- package/hover-dropdown/index.d.ts +1 -0
- package/hover-dropdown/src/hover-dropdown.directive.d.ts +41 -0
- package/index.d.ts +5 -0
- package/locale/index.d.ts +5 -0
- package/locale/src/currency.pipe.d.ts +14 -0
- package/locale/src/date.pipe.d.ts +14 -0
- package/locale/src/format-currency.d.ts +1 -0
- package/locale/src/format-date.d.ts +2 -0
- package/locale/src/is-empty.d.ts +1 -0
- package/locale/src/providers.d.ts +20 -0
- package/mat-busy-wheel/index.d.ts +4 -0
- package/mat-busy-wheel/src/busy-wheel-op.d.ts +65 -0
- package/mat-busy-wheel/src/busy-wheel.component.d.ts +12 -0
- package/mat-busy-wheel/src/busy-wheel.service.d.ts +42 -0
- package/mat-busy-wheel/src/host-busy-wheel.directive.d.ts +35 -0
- package/mat-context-menu/index.d.ts +1 -0
- package/mat-context-menu/src/mat-context-menu.component.d.ts +54 -0
- package/mat-entity-crud/index.d.ts +5 -0
- package/mat-entity-crud/src/default-config.d.ts +9 -0
- package/mat-entity-crud/src/form-view-host.component.d.ts +34 -0
- package/mat-entity-crud/src/mat-entity-crud-form-base.d.ts +95 -0
- package/mat-entity-crud/src/mat-entity-crud-internal-types.d.ts +66 -0
- package/mat-entity-crud/src/mat-entity-crud-types.d.ts +141 -0
- package/mat-entity-crud/src/mat-entity-crud.component.d.ts +267 -0
- package/mat-entity-crud/src/preview-host.component.d.ts +19 -0
- package/mat-entity-crud/src/preview-pane.component.d.ts +27 -0
- package/mat-entity-crud/src/providers.d.ts +3 -0
- package/mat-entity-list/index.d.ts +3 -0
- package/mat-entity-list/src/config.d.ts +6 -0
- package/mat-entity-list/src/mat-entity-list-types.d.ts +53 -0
- package/mat-entity-list/src/mat-entity-list.component.d.ts +209 -0
- package/mat-entity-list/src/providers.d.ts +3 -0
- package/mat-file-input/README.md +63 -0
- package/mat-file-input/index.d.ts +1 -0
- package/mat-file-input/src/mat-file-input.component.d.ts +58 -0
- package/mat-form-error/README.md +306 -0
- package/mat-form-error/index.d.ts +6 -0
- package/mat-form-error/src/locales/en.d.ts +4 -0
- package/mat-form-error/src/locales/hu.d.ts +4 -0
- package/mat-form-error/src/locales/index.d.ts +3 -0
- package/mat-form-error/src/locales/pt-br.d.ts +4 -0
- package/mat-form-error/src/ngx-error-list.component.d.ts +9 -0
- package/mat-form-error/src/ngx-mat-error-control.d.ts +17 -0
- package/mat-form-error/src/ngx-mat-error-def.directive.d.ts +30 -0
- package/mat-form-error/src/ngx-mat-errors-for-date-range-picker.directive.d.ts +8 -0
- package/mat-form-error/src/ngx-mat-errors.component.d.ts +23 -0
- package/mat-form-error/src/types.d.ts +68 -0
- package/mat-form-error/src/utils/coerce-to-observable.d.ts +3 -0
- package/mat-form-error/src/utils/distinct-until-error-changed.d.ts +2 -0
- package/mat-form-error/src/utils/find-error-for-control.d.ts +9 -0
- package/mat-form-error/src/utils/get-abstract-controls.d.ts +3 -0
- package/mat-form-error/src/utils/get-control-with-error.d.ts +3 -0
- package/mat-select-entity/index.d.ts +2 -0
- package/mat-select-entity/src/mat-select-entity.component.d.ts +207 -0
- package/mat-select-entity/src/providers.d.ts +9 -0
- package/mat-side-menu-layout/index.d.ts +6 -0
- package/mat-side-menu-layout/src/layout.service.d.ts +23 -0
- package/mat-side-menu-layout/src/mat-menu-layout.component.d.ts +39 -0
- package/mat-side-menu-layout/src/mat-menu-layout.module.d.ts +18 -0
- package/mat-side-menu-layout/src/mat-menu-list-item.component.d.ts +36 -0
- package/mat-side-menu-layout/src/mat-menu-pane.component.d.ts +66 -0
- package/mat-side-menu-layout/src/nav-item.d.ts +10 -0
- package/mat-tel-input/README.md +18 -0
- package/mat-tel-input/index.d.ts +2 -0
- package/mat-tel-input/src/country-codes.d.ts +5 -0
- package/mat-tel-input/src/mat-telephone.component.d.ts +129 -0
- package/mat-tel-input/src/providers.d.ts +38 -0
- package/ngx-helper.d.ts +2 -0
- package/package.json +114 -0
- package/public-api.d.ts +1 -0
- package/sideload/index.d.ts +1 -0
- package/sideload/src/sideload.d.ts +17 -0
- package/stationary-with-line-items/index.d.ts +1 -0
- package/stationary-with-line-items/src/stationary-with-line-items.component.d.ts +74 -0
|
@@ -0,0 +1,854 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { InjectionToken, input, EventEmitter, computed, Component, ChangeDetectionStrategy, Optional, Inject, Self, ViewChild, Input, Output, HostBinding } from '@angular/core';
|
|
3
|
+
import { coerceBooleanProperty } from '@angular/cdk/coercion';
|
|
4
|
+
import * as i3 from '@angular/common';
|
|
5
|
+
import { CommonModule } from '@angular/common';
|
|
6
|
+
import * as i1 from '@angular/common/http';
|
|
7
|
+
import { HttpContextToken, HttpParams, HttpContext } from '@angular/common/http';
|
|
8
|
+
import * as i2 from '@angular/forms';
|
|
9
|
+
import { Validators, FormsModule, ReactiveFormsModule } from '@angular/forms';
|
|
10
|
+
import * as i7 from '@angular/material/form-field';
|
|
11
|
+
import { MAT_FORM_FIELD, MatFormFieldControl } from '@angular/material/form-field';
|
|
12
|
+
import * as i4 from '@angular/material/select';
|
|
13
|
+
import { MatSelect, MatSelectModule } from '@angular/material/select';
|
|
14
|
+
import * as i6 from 'ngx-mat-select-search';
|
|
15
|
+
import { NgxMatSelectSearchModule } from 'ngx-mat-select-search';
|
|
16
|
+
import { Subject, BehaviorSubject, combineLatest, debounceTime, takeUntil, switchMap, of, tap } from 'rxjs';
|
|
17
|
+
import { getNgxHelperConfig } from '@smallpearl/ngx-helper/core';
|
|
18
|
+
import { plural } from 'pluralize';
|
|
19
|
+
import { camelCase } from 'lodash';
|
|
20
|
+
import * as i5 from '@angular/material/core';
|
|
21
|
+
|
|
22
|
+
const SP_MAT_SELECT_ENTITY_CONFIG = new InjectionToken('SPMatSelectEntityConfig');
|
|
23
|
+
|
|
24
|
+
const SP_MAT_SELECT_ENTITY_HTTP_CONTEXT = new HttpContextToken(() => ({
|
|
25
|
+
entityName: '',
|
|
26
|
+
entityNamePlural: '',
|
|
27
|
+
endpoint: '',
|
|
28
|
+
}));
|
|
29
|
+
const DEFAULT_SP_MAT_SELECT_ENTITY_CONFIG = {
|
|
30
|
+
i18n: {
|
|
31
|
+
search: 'Search',
|
|
32
|
+
notFound: 'Not found',
|
|
33
|
+
addItem: 'New Item',
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* This is a generic component to display a <mat-select> for a FK field
|
|
38
|
+
* where the select's options are dynamically loaded from the server using
|
|
39
|
+
* the given url (or URL). The objects thus retrieved should have a
|
|
40
|
+
* unique 'id' field that will be used as the value of each `option` element.
|
|
41
|
+
* Therefore upon selection of an `option` element, the `select` value will
|
|
42
|
+
* be set to the object's `id` property. By default 'id' is used as its id,
|
|
43
|
+
* but this can be customized by specifying the `idKey' property value.
|
|
44
|
+
*/
|
|
45
|
+
class SPMatSelectEntityComponent {
|
|
46
|
+
http;
|
|
47
|
+
cdr;
|
|
48
|
+
_elementRef;
|
|
49
|
+
injector;
|
|
50
|
+
_formField;
|
|
51
|
+
ngControl;
|
|
52
|
+
config;
|
|
53
|
+
// We cache the entities that we fetch from remote here. Cache is indexed
|
|
54
|
+
// by the endpoint. Each endpoint also keeps a refCount, which is incremented
|
|
55
|
+
// for each instance of the component using the same endpoint. When this
|
|
56
|
+
// refcount reaches 0, the endpoint is removed from the cache.
|
|
57
|
+
//
|
|
58
|
+
// This mechanism is to suppress multiple fetches from the remote from the
|
|
59
|
+
// same endpoint as that can occur if a form has multiple instances of
|
|
60
|
+
// this component, with the same endpoint.
|
|
61
|
+
static _entitiesCache = new Map();
|
|
62
|
+
matSel;
|
|
63
|
+
// REQUIRED PROPERTIES //
|
|
64
|
+
/**
|
|
65
|
+
* Entity label function. Given an entity return its natural label
|
|
66
|
+
* to display to the user.
|
|
67
|
+
*/
|
|
68
|
+
entityLabelFn;
|
|
69
|
+
// OPTIONAL PROPERTIES //
|
|
70
|
+
/**
|
|
71
|
+
* Entity filter function - return a boolean if the entity is to be included
|
|
72
|
+
* in the filtered entities list.
|
|
73
|
+
* @param entity: TEntity object to test for 'search' string.
|
|
74
|
+
* @param search - search string
|
|
75
|
+
*/
|
|
76
|
+
entityFilterFn;
|
|
77
|
+
/**
|
|
78
|
+
* Entity idKey, if idKey is different from the default 'id'.
|
|
79
|
+
*/
|
|
80
|
+
idKey = 'id';
|
|
81
|
+
/**
|
|
82
|
+
* URL of the remote from where entities are to be loaded.
|
|
83
|
+
* This won't be used if `loadFromRemoteFn` is specified.
|
|
84
|
+
*/
|
|
85
|
+
url;
|
|
86
|
+
/**
|
|
87
|
+
* Parameters to be added to the HTTP request to retrieve data from
|
|
88
|
+
* remote. This won't be used if `loadFromRemoteFn` is specified.
|
|
89
|
+
*/
|
|
90
|
+
httpParams;
|
|
91
|
+
/**
|
|
92
|
+
* Function to load entities from remote.
|
|
93
|
+
*/
|
|
94
|
+
loadFromRemoteFn;
|
|
95
|
+
inlineNew = false;
|
|
96
|
+
/**
|
|
97
|
+
* Entity name, that is used to form the "New { item }" menu item if
|
|
98
|
+
* inlineNew=true. This is also used as the key of the object in GET response
|
|
99
|
+
* if the reponse JSON is not an array and rather an object, where the values
|
|
100
|
+
* are stored indexed by the server model name. For eg:-
|
|
101
|
+
*
|
|
102
|
+
* {
|
|
103
|
+
* 'customers': [
|
|
104
|
+
* {...},
|
|
105
|
+
* {...},
|
|
106
|
+
* {...},
|
|
107
|
+
* ]
|
|
108
|
+
* }
|
|
109
|
+
*/
|
|
110
|
+
entityName;
|
|
111
|
+
// Set to true to allow multiple option selection. The returned value
|
|
112
|
+
// would be an array of entity ids.
|
|
113
|
+
multiple = false;
|
|
114
|
+
/*
|
|
115
|
+
Whether to group options using <mat-optgroup></mat-optgroup>.
|
|
116
|
+
If set to true, the response from the server should be an array of
|
|
117
|
+
groups of TEntity objects, where each object is of the form:
|
|
118
|
+
[
|
|
119
|
+
{
|
|
120
|
+
id: <id>,
|
|
121
|
+
name|label: <>,
|
|
122
|
+
items|<plural_entityName>|<custom_key>: [
|
|
123
|
+
TEntity,
|
|
124
|
+
...
|
|
125
|
+
]
|
|
126
|
+
},
|
|
127
|
+
...
|
|
128
|
+
]
|
|
129
|
+
*/
|
|
130
|
+
group = false;
|
|
131
|
+
/**
|
|
132
|
+
* The group object key name under which options are stored. Defaults to
|
|
133
|
+
* 'items' or pluralized 'entityName'. Ideally the client class should
|
|
134
|
+
* explicitly set this property value.
|
|
135
|
+
*/
|
|
136
|
+
groupOptionsKey;
|
|
137
|
+
/**
|
|
138
|
+
* If groupOptions = true, specify this to provide accurate label for each
|
|
139
|
+
* group. If not specified, group label will be determined by looking up one
|
|
140
|
+
* of the standard fields - name, label or description - whichever comes
|
|
141
|
+
* first.
|
|
142
|
+
*/
|
|
143
|
+
groupLabelFn;
|
|
144
|
+
/**
|
|
145
|
+
* Sideload data key name.
|
|
146
|
+
*/
|
|
147
|
+
sideloadDataKey = input();
|
|
148
|
+
/**
|
|
149
|
+
* Parser function to return the list of entities from the GET response.
|
|
150
|
+
*/
|
|
151
|
+
responseParserFn = input();
|
|
152
|
+
selectionChange = new EventEmitter();
|
|
153
|
+
createNewItemSelected = new EventEmitter();
|
|
154
|
+
// allow per component customization
|
|
155
|
+
searchText;
|
|
156
|
+
notFoundText;
|
|
157
|
+
addItemText;
|
|
158
|
+
_sideloadDataKey = computed(() => {
|
|
159
|
+
if (this.sideloadDataKey()) {
|
|
160
|
+
return this.sideloadDataKey();
|
|
161
|
+
}
|
|
162
|
+
return this.entityName ? plural(camelCase(this.entityName)) : 'results';
|
|
163
|
+
});
|
|
164
|
+
_entities = new Map();
|
|
165
|
+
_groupedEntities = new Array();
|
|
166
|
+
stateChanges = new Subject();
|
|
167
|
+
focused = false;
|
|
168
|
+
touched = false;
|
|
169
|
+
selectValue;
|
|
170
|
+
// For storing last select value, which we use to restore the select's value
|
|
171
|
+
// to when New Item is selected. This ensures that when New Item is selected,
|
|
172
|
+
// the select's value remains the same. If the newly created item is to be
|
|
173
|
+
// set as the select's value, the corresponding TEntity has to be added
|
|
174
|
+
// to _entities (via addEntity()) and then selected by setting the
|
|
175
|
+
// corresponding formControl's value to the entity's id.
|
|
176
|
+
lastSelectValue;
|
|
177
|
+
searching = false;
|
|
178
|
+
filterStr = '';
|
|
179
|
+
filter$ = new BehaviorSubject('');
|
|
180
|
+
// ControlValueAccessor callback
|
|
181
|
+
onChanged = (_) => { };
|
|
182
|
+
onTouched = () => { };
|
|
183
|
+
matSelect;
|
|
184
|
+
filteredValues = new Subject();
|
|
185
|
+
filteredGroupedValues = new Subject();
|
|
186
|
+
destroy = new Subject();
|
|
187
|
+
loaded = false;
|
|
188
|
+
load$ = new BehaviorSubject(false);
|
|
189
|
+
static nextId = 0;
|
|
190
|
+
id = `sp-select-entity-${SPMatSelectEntityComponent.nextId++}`;
|
|
191
|
+
_placeholder;
|
|
192
|
+
ngxHelperConfig = getNgxHelperConfig();
|
|
193
|
+
constructor(http, cdr, _elementRef, injector, _formField, ngControl, config) {
|
|
194
|
+
this.http = http;
|
|
195
|
+
this.cdr = cdr;
|
|
196
|
+
this._elementRef = _elementRef;
|
|
197
|
+
this.injector = injector;
|
|
198
|
+
this._formField = _formField;
|
|
199
|
+
this.ngControl = ngControl;
|
|
200
|
+
this.config = config;
|
|
201
|
+
if (this.ngControl != null) {
|
|
202
|
+
this.ngControl.valueAccessor = this;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
ngOnInit() {
|
|
206
|
+
this._initStrings();
|
|
207
|
+
combineLatest([this.filter$.pipe(debounceTime(400)), this.load$])
|
|
208
|
+
.pipe(takeUntil(this.destroy), switchMap(([str, load]) => {
|
|
209
|
+
if (load && !this.loaded) {
|
|
210
|
+
this.searching = true;
|
|
211
|
+
this.cdr.detectChanges();
|
|
212
|
+
return this.loadFromRemote();
|
|
213
|
+
}
|
|
214
|
+
else {
|
|
215
|
+
return of(this.entities ?? []);
|
|
216
|
+
}
|
|
217
|
+
}), tap(() => {
|
|
218
|
+
if (this.group) {
|
|
219
|
+
this.filterGroupedValues(this.filterStr);
|
|
220
|
+
}
|
|
221
|
+
else {
|
|
222
|
+
this.filterValues(this.filterStr);
|
|
223
|
+
}
|
|
224
|
+
this.cdr.detectChanges();
|
|
225
|
+
}))
|
|
226
|
+
.subscribe();
|
|
227
|
+
}
|
|
228
|
+
ngOnDestroy() {
|
|
229
|
+
this.destroy.next();
|
|
230
|
+
this.removeFromCache();
|
|
231
|
+
this.stateChanges.complete();
|
|
232
|
+
}
|
|
233
|
+
ngAfterViewInit() {
|
|
234
|
+
// I'm not sure this is how this logic is right, but this seems to work.
|
|
235
|
+
// if (this.ngControl && this.ngControl.control?.validator) {
|
|
236
|
+
// const validator = this.ngControl.control.validator;
|
|
237
|
+
// const res = validator(this.ngControl.control);
|
|
238
|
+
// if (res && res['required']) {
|
|
239
|
+
// this.required = true;
|
|
240
|
+
// }
|
|
241
|
+
// }
|
|
242
|
+
}
|
|
243
|
+
_initStrings() {
|
|
244
|
+
const config = this.config ?? DEFAULT_SP_MAT_SELECT_ENTITY_CONFIG;
|
|
245
|
+
if (!this.searchText) {
|
|
246
|
+
this.searchText = config.i18n.search;
|
|
247
|
+
}
|
|
248
|
+
if (!this.notFoundText) {
|
|
249
|
+
this.notFoundText = config.i18n.notFound;
|
|
250
|
+
}
|
|
251
|
+
if (!this.addItemText) {
|
|
252
|
+
this.addItemText = config.i18n.addItem.replace(/\{\{\s*item\s*}}/, this.entityName ?? "**Item");
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
addEntity(entity) {
|
|
256
|
+
this._entities.set(entity[this.idKey], entity);
|
|
257
|
+
// So that the newly added entity will be added to the <mat-option> list.
|
|
258
|
+
this.filterValues(this.filterStr);
|
|
259
|
+
this.cdr.detectChanges();
|
|
260
|
+
}
|
|
261
|
+
get selectTriggerValue() {
|
|
262
|
+
if (this.selectValue) {
|
|
263
|
+
const firstSelected = Array.isArray(this.selectValue) ? this.selectValue[0] : this.selectValue;
|
|
264
|
+
const selectedEntity = this._entities.get(firstSelected);
|
|
265
|
+
return selectedEntity ? this.entityLabelFn(selectedEntity) : '';
|
|
266
|
+
}
|
|
267
|
+
return '';
|
|
268
|
+
}
|
|
269
|
+
get selectTriggerValueAsArray() {
|
|
270
|
+
return Array.isArray(this.selectValue) ? this.selectValue : [];
|
|
271
|
+
}
|
|
272
|
+
entityId(entity) {
|
|
273
|
+
return entity[this.idKey];
|
|
274
|
+
}
|
|
275
|
+
writeValue(entityId) {
|
|
276
|
+
if (Array.isArray(entityId)) {
|
|
277
|
+
if (this.multiple) {
|
|
278
|
+
const selectedValues = [];
|
|
279
|
+
entityId.forEach(id => {
|
|
280
|
+
if (this._entities.has(id)) {
|
|
281
|
+
selectedValues.push(id);
|
|
282
|
+
}
|
|
283
|
+
});
|
|
284
|
+
this.selectValue = selectedValues;
|
|
285
|
+
this.cdr.detectChanges();
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
else {
|
|
289
|
+
if (this._entities.has(entityId)) {
|
|
290
|
+
this.selectValue = entityId;
|
|
291
|
+
if (this.filterStr) {
|
|
292
|
+
this.filterStr = '';
|
|
293
|
+
this.filterValues(this.filterStr);
|
|
294
|
+
}
|
|
295
|
+
this.cdr.detectChanges();
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
registerOnChange(fn) {
|
|
300
|
+
this.onChanged = fn;
|
|
301
|
+
}
|
|
302
|
+
registerOnTouched(fn) {
|
|
303
|
+
this.onTouched = fn;
|
|
304
|
+
}
|
|
305
|
+
get entities() {
|
|
306
|
+
return Array.from(this._entities.values());
|
|
307
|
+
}
|
|
308
|
+
set entities(items) {
|
|
309
|
+
if (!this.group) {
|
|
310
|
+
items.forEach(item => {
|
|
311
|
+
this._entities.set(item[this.idKey], item);
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
else {
|
|
315
|
+
this._groupedEntities = items;
|
|
316
|
+
this._groupedEntities.forEach(group => {
|
|
317
|
+
const key = this.groupEntitiesKey();
|
|
318
|
+
const groupEntities = group[key];
|
|
319
|
+
group['__items__'] = groupEntities;
|
|
320
|
+
groupEntities.forEach(item => {
|
|
321
|
+
this._entities.set(item[this.idKey], item);
|
|
322
|
+
});
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
get value() {
|
|
327
|
+
return this.selectValue;
|
|
328
|
+
}
|
|
329
|
+
set value(val) {
|
|
330
|
+
this.selectValue = val;
|
|
331
|
+
this.stateChanges.next();
|
|
332
|
+
}
|
|
333
|
+
get shouldLabelFloat() {
|
|
334
|
+
return this.focused || !this.empty;
|
|
335
|
+
}
|
|
336
|
+
userAriaDescribedBy;
|
|
337
|
+
get placeholder() {
|
|
338
|
+
return this._placeholder;
|
|
339
|
+
}
|
|
340
|
+
set placeholder(value) {
|
|
341
|
+
this._placeholder = value;
|
|
342
|
+
this.stateChanges.next();
|
|
343
|
+
}
|
|
344
|
+
get required() {
|
|
345
|
+
return this._required ?? this.ngControl?.control?.hasValidator(Validators.required);
|
|
346
|
+
}
|
|
347
|
+
set required(req) {
|
|
348
|
+
this._required = coerceBooleanProperty(req);
|
|
349
|
+
this.stateChanges.next();
|
|
350
|
+
}
|
|
351
|
+
// Deliberately 'undefined' so that `get required()` will return the state
|
|
352
|
+
// from ngControl's validators.
|
|
353
|
+
_required;
|
|
354
|
+
get disabled() {
|
|
355
|
+
return this._disabled ?? this.ngControl?.control?.disabled;
|
|
356
|
+
}
|
|
357
|
+
set disabled(value) {
|
|
358
|
+
const disabled = coerceBooleanProperty(value);
|
|
359
|
+
;
|
|
360
|
+
if (disabled !== this._disabled) {
|
|
361
|
+
this.setDisabledState(disabled);
|
|
362
|
+
this.stateChanges.next();
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
// Same as `_required`, deliberately `undefined` by default.
|
|
366
|
+
_disabled;
|
|
367
|
+
get empty() {
|
|
368
|
+
// TODO
|
|
369
|
+
return !this.value;
|
|
370
|
+
}
|
|
371
|
+
get errorState() {
|
|
372
|
+
// TODO
|
|
373
|
+
return false;
|
|
374
|
+
}
|
|
375
|
+
onFocusIn(event) {
|
|
376
|
+
if (!this.focused) {
|
|
377
|
+
this.focused = true;
|
|
378
|
+
this.stateChanges.next();
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
onFocusOut(event) {
|
|
382
|
+
if (!this._elementRef.nativeElement.contains(event.relatedTarget)) {
|
|
383
|
+
this.touched = true;
|
|
384
|
+
this.focused = false;
|
|
385
|
+
this.onTouched();
|
|
386
|
+
this.stateChanges.next();
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
setDescribedByIds(ids) { }
|
|
390
|
+
onContainerClick() {
|
|
391
|
+
// this._focusMonitor.focusVia(this.countrySelect, 'program');
|
|
392
|
+
// if (this.parts.controls.national.valid) {
|
|
393
|
+
// this._focusMonitor.focusVia(this.nationalInput, 'program');
|
|
394
|
+
// } else if (this.parts.controls.country.valid) {
|
|
395
|
+
// this._focusMonitor.focusVia(this.nationalInput, 'program');
|
|
396
|
+
// // } else if (this.parts.controls.national.valid) {
|
|
397
|
+
// // this._focusMonitor.focusVia(this.nationalInput, 'program');
|
|
398
|
+
// } else {
|
|
399
|
+
// this._focusMonitor.focusVia(this.countrySelect, 'program');
|
|
400
|
+
// }
|
|
401
|
+
}
|
|
402
|
+
setDisabledState(isDisabled) {
|
|
403
|
+
this._disabled = isDisabled;
|
|
404
|
+
if (this.matSelect) {
|
|
405
|
+
this.matSelect.setDisabledState(isDisabled);
|
|
406
|
+
this.cdr.detectChanges();
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
onSelectOpened(ev) {
|
|
410
|
+
// Store the current select value so that we can restore it if user
|
|
411
|
+
// eventually selects 'New Item' option.
|
|
412
|
+
this.lastSelectValue = this.selectValue;
|
|
413
|
+
// If values have not been loaded from remote, trigger a load.
|
|
414
|
+
if (!this.loaded) {
|
|
415
|
+
this.load$.next(true); // this will trigger the loadFromRemote() call.
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
onSelectionChange(ev) {
|
|
419
|
+
// console.log('SelectionChange - sel:', ev);
|
|
420
|
+
if (Array.isArray(ev.value)) {
|
|
421
|
+
this.selectValue = ev.value;
|
|
422
|
+
this.onTouched();
|
|
423
|
+
this.onChanged(ev.value);
|
|
424
|
+
const selectedEntities = ev.value.map(id => this._entities.get(id));
|
|
425
|
+
this.selectionChange.emit(selectedEntities);
|
|
426
|
+
}
|
|
427
|
+
else {
|
|
428
|
+
if (ev.value !== '0') {
|
|
429
|
+
this.selectValue = ev.value;
|
|
430
|
+
this.onTouched();
|
|
431
|
+
this.onChanged(ev.value);
|
|
432
|
+
this.selectionChange.emit(this._entities.get(ev.value));
|
|
433
|
+
}
|
|
434
|
+
else {
|
|
435
|
+
// New Item activated, return value to previous value. We track
|
|
436
|
+
// previous value via 'lastSelectValue' member which is updated
|
|
437
|
+
// whenever the select is opened.
|
|
438
|
+
if (this.ngControl) {
|
|
439
|
+
this.ngControl.control?.setValue(this.lastSelectValue);
|
|
440
|
+
}
|
|
441
|
+
ev.source.value = this.lastSelectValue;
|
|
442
|
+
this.createNewItemSelected.emit();
|
|
443
|
+
this.cdr.detectChanges();
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
filterValues(search) {
|
|
448
|
+
const searchLwr = search.toLocaleLowerCase();
|
|
449
|
+
const entities = this.entities;
|
|
450
|
+
if (!entities) {
|
|
451
|
+
return;
|
|
452
|
+
}
|
|
453
|
+
if (!search) {
|
|
454
|
+
this.filteredValues.next(entities.slice());
|
|
455
|
+
}
|
|
456
|
+
else {
|
|
457
|
+
this.filteredValues.next(entities.filter((member) => {
|
|
458
|
+
if (this.entityFilterFn) {
|
|
459
|
+
return this.entityFilterFn(member, search);
|
|
460
|
+
}
|
|
461
|
+
return this.entityLabelFn(member).toLocaleLowerCase().includes(searchLwr);
|
|
462
|
+
}));
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
/**
|
|
466
|
+
* Filtering grouped entities logic works like this. If the search string
|
|
467
|
+
* matches a group label, the entire group is to be included in the results.
|
|
468
|
+
* However, if the search string only matches certain entities, only those
|
|
469
|
+
* groups are to be included and within those groups, only entities whose
|
|
470
|
+
* label matches the search string are to be included in the result set.
|
|
471
|
+
* @param search
|
|
472
|
+
* @returns
|
|
473
|
+
*/
|
|
474
|
+
filterGroupedValues(search) {
|
|
475
|
+
const searchLwr = search.toLocaleLowerCase();
|
|
476
|
+
const groups = this._groupedEntities;
|
|
477
|
+
if (!groups) {
|
|
478
|
+
return;
|
|
479
|
+
}
|
|
480
|
+
if (!search) {
|
|
481
|
+
const groupsCopy = groups.slice();
|
|
482
|
+
this.filteredGroupedValues.next(groupsCopy);
|
|
483
|
+
}
|
|
484
|
+
else {
|
|
485
|
+
const groupEntitiesKey = this.groupEntitiesKey();
|
|
486
|
+
const groups = this._groupedEntities.map(ge => {
|
|
487
|
+
const label = this.groupLabel(ge);
|
|
488
|
+
if (label.toLocaleLowerCase().includes(searchLwr)) {
|
|
489
|
+
return { ...ge };
|
|
490
|
+
}
|
|
491
|
+
else {
|
|
492
|
+
const groupEntities = ge.__items__?.filter(e => this.entityLabelFn(e).toLocaleLowerCase().includes(searchLwr));
|
|
493
|
+
const ret = {
|
|
494
|
+
...ge
|
|
495
|
+
};
|
|
496
|
+
ret['__items__'] = groupEntities ?? [];
|
|
497
|
+
return ret;
|
|
498
|
+
}
|
|
499
|
+
});
|
|
500
|
+
// filter out groups with no entities
|
|
501
|
+
// console.log(`Groups: ${JSON.stringify(groups)}`);
|
|
502
|
+
this.filteredGroupedValues.next(groups.filter((group) => Array.isArray(group[groupEntitiesKey]) &&
|
|
503
|
+
group['__items__'].length > 0));
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
loadFromRemote() {
|
|
507
|
+
if (!this.url && !this.loadFromRemoteFn) {
|
|
508
|
+
// If user had initialized entities, they will be dispalyed
|
|
509
|
+
// in the options list. If not, options would be empty.
|
|
510
|
+
return of(this.group ? this.groupEntities : this.entities);
|
|
511
|
+
}
|
|
512
|
+
let cacheKey;
|
|
513
|
+
let obs;
|
|
514
|
+
if (this.loadFromRemoteFn) {
|
|
515
|
+
obs = this.loadFromRemoteFn(this.injector);
|
|
516
|
+
}
|
|
517
|
+
else {
|
|
518
|
+
let params;
|
|
519
|
+
if (this.httpParams) {
|
|
520
|
+
params = new HttpParams({
|
|
521
|
+
fromString: this.httpParams.toString()
|
|
522
|
+
});
|
|
523
|
+
}
|
|
524
|
+
else {
|
|
525
|
+
params = new HttpParams();
|
|
526
|
+
}
|
|
527
|
+
params = params.set('paginate', false);
|
|
528
|
+
cacheKey = this.getCacheKey();
|
|
529
|
+
if (this.existsInCache()) {
|
|
530
|
+
obs = of(this.getFromCache());
|
|
531
|
+
}
|
|
532
|
+
else {
|
|
533
|
+
obs = this.http.get(this.url, {
|
|
534
|
+
context: this.getHttpReqContext(),
|
|
535
|
+
params,
|
|
536
|
+
});
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
return obs.pipe(tap((entities) => {
|
|
540
|
+
this.searching = false; // remote loading done, will hide the loading wheel
|
|
541
|
+
// Handle DRF paginated response
|
|
542
|
+
const responseParserFn = this.responseParserFn();
|
|
543
|
+
if (responseParserFn) {
|
|
544
|
+
entities = responseParserFn(entities);
|
|
545
|
+
}
|
|
546
|
+
else {
|
|
547
|
+
if (!Array.isArray(entities) &&
|
|
548
|
+
entities['results'] &&
|
|
549
|
+
Array.isArray(entities['results'])) {
|
|
550
|
+
entities = entities['results'];
|
|
551
|
+
}
|
|
552
|
+
else if ( // sideloaded response, where entities are usually provided in 'entityName'
|
|
553
|
+
this._sideloadDataKey() &&
|
|
554
|
+
!Array.isArray(entities) &&
|
|
555
|
+
typeof entities === 'object' &&
|
|
556
|
+
entities[this._sideloadDataKey()] &&
|
|
557
|
+
Array.isArray(entities[this._sideloadDataKey()])) {
|
|
558
|
+
entities = entities[this._sideloadDataKey()];
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
if (Array.isArray(entities)) {
|
|
562
|
+
this.entities = entities;
|
|
563
|
+
// if (this.group) {
|
|
564
|
+
// this._groupedEntities = entities as EntityGroup<TEntity>[];
|
|
565
|
+
// } else {
|
|
566
|
+
// this.entities = entities;
|
|
567
|
+
// }
|
|
568
|
+
}
|
|
569
|
+
this.loaded = true;
|
|
570
|
+
this.addToCache(entities);
|
|
571
|
+
this.cdr.detectChanges();
|
|
572
|
+
}));
|
|
573
|
+
}
|
|
574
|
+
groupLabel(group) {
|
|
575
|
+
if (this.groupLabelFn) {
|
|
576
|
+
return this.groupLabelFn(group);
|
|
577
|
+
}
|
|
578
|
+
const standardLabelFields = ['name', 'label', 'desc', 'description'];
|
|
579
|
+
for (let index = 0; index < standardLabelFields.length; index++) {
|
|
580
|
+
const labelField = standardLabelFields[index];
|
|
581
|
+
if (group[labelField]) {
|
|
582
|
+
return group[labelField];
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
return `Group ${String(group.id)}`;
|
|
586
|
+
}
|
|
587
|
+
groupEntities(group) {
|
|
588
|
+
const key = this.groupEntitiesKey();
|
|
589
|
+
console.log(`groupEntities - group: ${JSON.stringify(group)}, key: ${key}`);
|
|
590
|
+
return group[this.groupEntitiesKey()] ?? [];
|
|
591
|
+
}
|
|
592
|
+
groupEntitiesKey() {
|
|
593
|
+
return this.groupOptionsKey ? this.groupOptionsKey
|
|
594
|
+
: (this.entityName ? plural(this.entityName.toLocaleLowerCase()) : 'items');
|
|
595
|
+
}
|
|
596
|
+
existsInCache() {
|
|
597
|
+
const cacheKey = this.getCacheKey();
|
|
598
|
+
if (cacheKey) {
|
|
599
|
+
return SPMatSelectEntityComponent._entitiesCache.has(cacheKey);
|
|
600
|
+
}
|
|
601
|
+
return false;
|
|
602
|
+
}
|
|
603
|
+
getCacheKey() {
|
|
604
|
+
if (!this.loadFromRemoteFn) {
|
|
605
|
+
let params;
|
|
606
|
+
if (this.httpParams) {
|
|
607
|
+
params = new HttpParams({
|
|
608
|
+
fromString: this.httpParams.toString()
|
|
609
|
+
});
|
|
610
|
+
}
|
|
611
|
+
else {
|
|
612
|
+
params = new HttpParams();
|
|
613
|
+
}
|
|
614
|
+
// params = params.set('paginate', false)
|
|
615
|
+
return `${this.url}?${params.toString()}`;
|
|
616
|
+
}
|
|
617
|
+
return ''; // empty string evalutes to boolean(false)
|
|
618
|
+
}
|
|
619
|
+
getFromCache() {
|
|
620
|
+
const cacheKey = this.getCacheKey();
|
|
621
|
+
if (cacheKey && SPMatSelectEntityComponent._entitiesCache.has(cacheKey)) {
|
|
622
|
+
return SPMatSelectEntityComponent._entitiesCache.get(cacheKey)?.entities;
|
|
623
|
+
}
|
|
624
|
+
return [];
|
|
625
|
+
}
|
|
626
|
+
addToCache(entities) {
|
|
627
|
+
const cacheKey = this.getCacheKey();
|
|
628
|
+
if (cacheKey) {
|
|
629
|
+
if (!SPMatSelectEntityComponent._entitiesCache.has(cacheKey)) {
|
|
630
|
+
SPMatSelectEntityComponent._entitiesCache.set(cacheKey, { refCount: 0, entities });
|
|
631
|
+
}
|
|
632
|
+
const cacheEntry = SPMatSelectEntityComponent._entitiesCache.get(cacheKey);
|
|
633
|
+
cacheEntry.refCount += 1;
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
removeFromCache() {
|
|
637
|
+
const cacheKey = this.getCacheKey();
|
|
638
|
+
if (cacheKey) {
|
|
639
|
+
const cacheEntry = SPMatSelectEntityComponent._entitiesCache.get(cacheKey);
|
|
640
|
+
if (cacheEntry) {
|
|
641
|
+
cacheEntry.refCount -= 1;
|
|
642
|
+
if (cacheEntry.refCount <= 0) {
|
|
643
|
+
SPMatSelectEntityComponent._entitiesCache.delete(cacheKey);
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
getHttpReqContext() {
|
|
649
|
+
const context = new HttpContext();
|
|
650
|
+
const entityName = this.entityName;
|
|
651
|
+
context.set(SP_MAT_SELECT_ENTITY_HTTP_CONTEXT, {
|
|
652
|
+
entityName: this.entityName ?? '',
|
|
653
|
+
entityNamePlural: this.entityName ? plural(this.entityName) : '',
|
|
654
|
+
endpoint: this.url
|
|
655
|
+
});
|
|
656
|
+
return context;
|
|
657
|
+
}
|
|
658
|
+
/** @nocollapse */ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: SPMatSelectEntityComponent, deps: [{ token: i1.HttpClient }, { token: i0.ChangeDetectorRef }, { token: i0.ElementRef }, { token: i0.Injector }, { token: MAT_FORM_FIELD, optional: true }, { token: i2.NgControl, optional: true, self: true }, { token: SP_MAT_SELECT_ENTITY_CONFIG, optional: true }], target: i0.ɵɵFactoryTarget.Component });
|
|
659
|
+
/** @nocollapse */ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.1.6", type: SPMatSelectEntityComponent, isStandalone: true, selector: "sp-mat-select-entity", inputs: { entityLabelFn: { classPropertyName: "entityLabelFn", publicName: "entityLabelFn", isSignal: false, isRequired: true, transformFunction: null }, entityFilterFn: { classPropertyName: "entityFilterFn", publicName: "entityFilterFn", isSignal: false, isRequired: false, transformFunction: null }, idKey: { classPropertyName: "idKey", publicName: "idKey", isSignal: false, isRequired: false, transformFunction: null }, url: { classPropertyName: "url", publicName: "url", isSignal: false, isRequired: false, transformFunction: null }, httpParams: { classPropertyName: "httpParams", publicName: "httpParams", isSignal: false, isRequired: false, transformFunction: null }, loadFromRemoteFn: { classPropertyName: "loadFromRemoteFn", publicName: "loadFromRemoteFn", isSignal: false, isRequired: false, transformFunction: null }, inlineNew: { classPropertyName: "inlineNew", publicName: "inlineNew", isSignal: false, isRequired: false, transformFunction: null }, entityName: { classPropertyName: "entityName", publicName: "entityName", isSignal: false, isRequired: false, transformFunction: null }, multiple: { classPropertyName: "multiple", publicName: "multiple", isSignal: false, isRequired: false, transformFunction: null }, group: { classPropertyName: "group", publicName: "group", isSignal: false, isRequired: false, transformFunction: null }, groupOptionsKey: { classPropertyName: "groupOptionsKey", publicName: "groupOptionsKey", isSignal: false, isRequired: false, transformFunction: null }, groupLabelFn: { classPropertyName: "groupLabelFn", publicName: "groupLabelFn", isSignal: false, isRequired: false, transformFunction: null }, sideloadDataKey: { classPropertyName: "sideloadDataKey", publicName: "sideloadDataKey", isSignal: true, isRequired: false, transformFunction: null }, responseParserFn: { classPropertyName: "responseParserFn", publicName: "responseParserFn", isSignal: true, isRequired: false, transformFunction: null }, searchText: { classPropertyName: "searchText", publicName: "searchText", isSignal: false, isRequired: false, transformFunction: null }, notFoundText: { classPropertyName: "notFoundText", publicName: "notFoundText", isSignal: false, isRequired: false, transformFunction: null }, addItemText: { classPropertyName: "addItemText", publicName: "addItemText", isSignal: false, isRequired: false, transformFunction: null }, entities: { classPropertyName: "entities", publicName: "entities", isSignal: false, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: false, isRequired: false, transformFunction: null }, userAriaDescribedBy: { classPropertyName: "userAriaDescribedBy", publicName: "aria-describedby", isSignal: false, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: false, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: false, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: false, isRequired: false, transformFunction: null } }, outputs: { selectionChange: "selectionChange", createNewItemSelected: "createNewItemSelected" }, host: { properties: { "id": "this.id" } }, providers: [{ provide: MatFormFieldControl, useExisting: SPMatSelectEntityComponent }], viewQueries: [{ propertyName: "matSel", first: true, predicate: MatSelect, descendants: true }, { propertyName: "matSelect", first: true, predicate: MatSelect, descendants: true }], ngImport: i0, template: `
|
|
660
|
+
<mat-select
|
|
661
|
+
[placeholder]="placeholder"
|
|
662
|
+
(opened)="onSelectOpened($event)"
|
|
663
|
+
(selectionChange)="onSelectionChange($event)"
|
|
664
|
+
[multiple]="multiple"
|
|
665
|
+
[(ngModel)]="selectValue"
|
|
666
|
+
>
|
|
667
|
+
<mat-select-trigger>
|
|
668
|
+
{{ selectTriggerValue }}
|
|
669
|
+
@if (selectTriggerValueAsArray.length > 1) {
|
|
670
|
+
<span class="addl-selection-count"> (+{{ selectTriggerValueAsArray.length - 1 }}) </span>
|
|
671
|
+
}
|
|
672
|
+
</mat-select-trigger>
|
|
673
|
+
|
|
674
|
+
<mat-option>
|
|
675
|
+
<ngx-mat-select-search
|
|
676
|
+
[(ngModel)]="filterStr"
|
|
677
|
+
(ngModelChange)="this.filter$.next($event)"
|
|
678
|
+
[placeholderLabel]="searchText"
|
|
679
|
+
[noEntriesFoundLabel]="notFoundText"
|
|
680
|
+
[searching]="searching"
|
|
681
|
+
>
|
|
682
|
+
</ngx-mat-select-search>
|
|
683
|
+
</mat-option>
|
|
684
|
+
|
|
685
|
+
<ng-container *ngIf="!group; else groupedOptions">
|
|
686
|
+
<span *ngIf="(filteredValues | async) as entities">
|
|
687
|
+
<mat-option class="sel-entity-option" *ngFor="let entity of entities" [value]="entityId(entity)">
|
|
688
|
+
{{ entityLabelFn(entity) }}
|
|
689
|
+
</mat-option>
|
|
690
|
+
</span>
|
|
691
|
+
</ng-container>
|
|
692
|
+
<ng-template #groupedOptions>
|
|
693
|
+
<span *ngIf="(filteredGroupedValues | async) as groups">
|
|
694
|
+
@for (group of groups; track groupLabel(group)) {
|
|
695
|
+
<mat-optgroup [label]="groupLabel(group)">
|
|
696
|
+
@for (entity of group.__items__; track entityId(entity)) {
|
|
697
|
+
<mat-option class="sel-entity-option" [value]="entityId(entity)">
|
|
698
|
+
{{ entityLabelFn(entity) }}
|
|
699
|
+
</mat-option>
|
|
700
|
+
}
|
|
701
|
+
</mat-optgroup>
|
|
702
|
+
}
|
|
703
|
+
</span>
|
|
704
|
+
</ng-template>
|
|
705
|
+
|
|
706
|
+
<mat-option *ngIf="!multiple && inlineNew" class="add-item-option" value="0" (click)="$event.stopPropagation()"
|
|
707
|
+
>⊕ {{ addItemText }}</mat-option
|
|
708
|
+
>
|
|
709
|
+
</mat-select>
|
|
710
|
+
`, isInline: true, styles: [".add-item-option{padding-top:2px;border-top:1px solid gray}.addl-selection-count{opacity:.75;font-size:.8em}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i3.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i3.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "pipe", type: i3.AsyncPipe, name: "async" }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i4.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "directive", type: i4.MatSelectTrigger, selector: "mat-select-trigger" }, { kind: "component", type: i5.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "component", type: i5.MatOptgroup, selector: "mat-optgroup", inputs: ["label", "disabled"], exportAs: ["matOptgroup"] }, { kind: "ngmodule", type: NgxMatSelectSearchModule }, { kind: "component", type: i6.MatSelectSearchComponent, selector: "ngx-mat-select-search", inputs: ["placeholderLabel", "type", "closeIcon", "closeSvgIcon", "noEntriesFoundLabel", "clearSearchInput", "searching", "disableInitialFocus", "enableClearOnEscapePressed", "preventHomeEndKeyPropagation", "disableScrollToActiveOnOptionsChanged", "ariaLabel", "showToggleAllCheckbox", "toggleAllCheckboxChecked", "toggleAllCheckboxIndeterminate", "toggleAllCheckboxTooltipMessage", "toggleAllCheckboxTooltipPosition", "hideClearSearchButton", "alwaysRestoreSelectedOptionsMulti", "recreateValuesArray"], outputs: ["toggleAll"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
711
|
+
}
|
|
712
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: SPMatSelectEntityComponent, decorators: [{
|
|
713
|
+
type: Component,
|
|
714
|
+
args: [{ selector: 'sp-mat-select-entity', template: `
|
|
715
|
+
<mat-select
|
|
716
|
+
[placeholder]="placeholder"
|
|
717
|
+
(opened)="onSelectOpened($event)"
|
|
718
|
+
(selectionChange)="onSelectionChange($event)"
|
|
719
|
+
[multiple]="multiple"
|
|
720
|
+
[(ngModel)]="selectValue"
|
|
721
|
+
>
|
|
722
|
+
<mat-select-trigger>
|
|
723
|
+
{{ selectTriggerValue }}
|
|
724
|
+
@if (selectTriggerValueAsArray.length > 1) {
|
|
725
|
+
<span class="addl-selection-count"> (+{{ selectTriggerValueAsArray.length - 1 }}) </span>
|
|
726
|
+
}
|
|
727
|
+
</mat-select-trigger>
|
|
728
|
+
|
|
729
|
+
<mat-option>
|
|
730
|
+
<ngx-mat-select-search
|
|
731
|
+
[(ngModel)]="filterStr"
|
|
732
|
+
(ngModelChange)="this.filter$.next($event)"
|
|
733
|
+
[placeholderLabel]="searchText"
|
|
734
|
+
[noEntriesFoundLabel]="notFoundText"
|
|
735
|
+
[searching]="searching"
|
|
736
|
+
>
|
|
737
|
+
</ngx-mat-select-search>
|
|
738
|
+
</mat-option>
|
|
739
|
+
|
|
740
|
+
<ng-container *ngIf="!group; else groupedOptions">
|
|
741
|
+
<span *ngIf="(filteredValues | async) as entities">
|
|
742
|
+
<mat-option class="sel-entity-option" *ngFor="let entity of entities" [value]="entityId(entity)">
|
|
743
|
+
{{ entityLabelFn(entity) }}
|
|
744
|
+
</mat-option>
|
|
745
|
+
</span>
|
|
746
|
+
</ng-container>
|
|
747
|
+
<ng-template #groupedOptions>
|
|
748
|
+
<span *ngIf="(filteredGroupedValues | async) as groups">
|
|
749
|
+
@for (group of groups; track groupLabel(group)) {
|
|
750
|
+
<mat-optgroup [label]="groupLabel(group)">
|
|
751
|
+
@for (entity of group.__items__; track entityId(entity)) {
|
|
752
|
+
<mat-option class="sel-entity-option" [value]="entityId(entity)">
|
|
753
|
+
{{ entityLabelFn(entity) }}
|
|
754
|
+
</mat-option>
|
|
755
|
+
}
|
|
756
|
+
</mat-optgroup>
|
|
757
|
+
}
|
|
758
|
+
</span>
|
|
759
|
+
</ng-template>
|
|
760
|
+
|
|
761
|
+
<mat-option *ngIf="!multiple && inlineNew" class="add-item-option" value="0" (click)="$event.stopPropagation()"
|
|
762
|
+
>⊕ {{ addItemText }}</mat-option
|
|
763
|
+
>
|
|
764
|
+
</mat-select>
|
|
765
|
+
`, changeDetection: ChangeDetectionStrategy.OnPush, imports: [CommonModule, FormsModule, ReactiveFormsModule, MatSelectModule, NgxMatSelectSearchModule], providers: [{ provide: MatFormFieldControl, useExisting: SPMatSelectEntityComponent }], styles: [".add-item-option{padding-top:2px;border-top:1px solid gray}.addl-selection-count{opacity:.75;font-size:.8em}\n"] }]
|
|
766
|
+
}], ctorParameters: () => [{ type: i1.HttpClient }, { type: i0.ChangeDetectorRef }, { type: i0.ElementRef }, { type: i0.Injector }, { type: i7.MatFormField, decorators: [{
|
|
767
|
+
type: Optional
|
|
768
|
+
}, {
|
|
769
|
+
type: Inject,
|
|
770
|
+
args: [MAT_FORM_FIELD]
|
|
771
|
+
}] }, { type: i2.NgControl, decorators: [{
|
|
772
|
+
type: Optional
|
|
773
|
+
}, {
|
|
774
|
+
type: Self
|
|
775
|
+
}] }, { type: undefined, decorators: [{
|
|
776
|
+
type: Optional
|
|
777
|
+
}, {
|
|
778
|
+
type: Inject,
|
|
779
|
+
args: [SP_MAT_SELECT_ENTITY_CONFIG]
|
|
780
|
+
}] }], propDecorators: { matSel: [{
|
|
781
|
+
type: ViewChild,
|
|
782
|
+
args: [MatSelect]
|
|
783
|
+
}], entityLabelFn: [{
|
|
784
|
+
type: Input,
|
|
785
|
+
args: [{ required: true }]
|
|
786
|
+
}], entityFilterFn: [{
|
|
787
|
+
type: Input,
|
|
788
|
+
args: [{ required: false }]
|
|
789
|
+
}], idKey: [{
|
|
790
|
+
type: Input,
|
|
791
|
+
args: [{ required: false }]
|
|
792
|
+
}], url: [{
|
|
793
|
+
type: Input,
|
|
794
|
+
args: [{ required: false }]
|
|
795
|
+
}], httpParams: [{
|
|
796
|
+
type: Input,
|
|
797
|
+
args: [{ required: false }]
|
|
798
|
+
}], loadFromRemoteFn: [{
|
|
799
|
+
type: Input,
|
|
800
|
+
args: [{ required: false }]
|
|
801
|
+
}], inlineNew: [{
|
|
802
|
+
type: Input,
|
|
803
|
+
args: [{ required: false }]
|
|
804
|
+
}], entityName: [{
|
|
805
|
+
type: Input,
|
|
806
|
+
args: [{ required: false }]
|
|
807
|
+
}], multiple: [{
|
|
808
|
+
type: Input,
|
|
809
|
+
args: [{ required: false }]
|
|
810
|
+
}], group: [{
|
|
811
|
+
type: Input,
|
|
812
|
+
args: [{ required: false }]
|
|
813
|
+
}], groupOptionsKey: [{
|
|
814
|
+
type: Input,
|
|
815
|
+
args: [{ required: false }]
|
|
816
|
+
}], groupLabelFn: [{
|
|
817
|
+
type: Input,
|
|
818
|
+
args: [{ required: false }]
|
|
819
|
+
}], selectionChange: [{
|
|
820
|
+
type: Output
|
|
821
|
+
}], createNewItemSelected: [{
|
|
822
|
+
type: Output
|
|
823
|
+
}], searchText: [{
|
|
824
|
+
type: Input
|
|
825
|
+
}], notFoundText: [{
|
|
826
|
+
type: Input
|
|
827
|
+
}], addItemText: [{
|
|
828
|
+
type: Input
|
|
829
|
+
}], matSelect: [{
|
|
830
|
+
type: ViewChild,
|
|
831
|
+
args: [MatSelect]
|
|
832
|
+
}], id: [{
|
|
833
|
+
type: HostBinding
|
|
834
|
+
}], entities: [{
|
|
835
|
+
type: Input
|
|
836
|
+
}], value: [{
|
|
837
|
+
type: Input
|
|
838
|
+
}], userAriaDescribedBy: [{
|
|
839
|
+
type: Input,
|
|
840
|
+
args: ['aria-describedby']
|
|
841
|
+
}], placeholder: [{
|
|
842
|
+
type: Input
|
|
843
|
+
}], required: [{
|
|
844
|
+
type: Input
|
|
845
|
+
}], disabled: [{
|
|
846
|
+
type: Input
|
|
847
|
+
}] } });
|
|
848
|
+
|
|
849
|
+
/**
|
|
850
|
+
* Generated bundle index. Do not edit.
|
|
851
|
+
*/
|
|
852
|
+
|
|
853
|
+
export { SPMatSelectEntityComponent, SP_MAT_SELECT_ENTITY_CONFIG, SP_MAT_SELECT_ENTITY_HTTP_CONTEXT };
|
|
854
|
+
//# sourceMappingURL=smallpearl-ngx-helper-mat-select-entity.mjs.map
|