@memberjunction/ng-find-record 4.0.0 → 4.1.0
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 +56 -277
- package/package.json +6 -6
package/README.md
CHANGED
|
@@ -1,22 +1,6 @@
|
|
|
1
1
|
# @memberjunction/ng-find-record
|
|
2
2
|
|
|
3
|
-
An Angular component library for searching and selecting records from any MemberJunction entity.
|
|
4
|
-
|
|
5
|
-
## Purpose and Overview
|
|
6
|
-
|
|
7
|
-
The `@memberjunction/ng-find-record` package simplifies the process of implementing entity record search functionality in MemberJunction-based Angular applications. It provides a reusable component that can search any entity using the MemberJunction metadata system and RunView API, displaying results in a Kendo UI grid for easy selection.
|
|
8
|
-
|
|
9
|
-
## Features
|
|
10
|
-
|
|
11
|
-
- **Entity-Agnostic**: Works with any MemberJunction entity without modification
|
|
12
|
-
- **Debounced Search**: Real-time search with configurable debounce time (default: 300ms)
|
|
13
|
-
- **Grid Display**: Results displayed in a searchable, sortable Kendo UI grid
|
|
14
|
-
- **Customizable Fields**: Configure which fields to display in the results grid
|
|
15
|
-
- **Dialog Integration**: Optional dialog wrapper for modal usage scenarios
|
|
16
|
-
- **Event Handling**: Events for record selection and dialog closure
|
|
17
|
-
- **Loading States**: Visual feedback during search operations
|
|
18
|
-
- **TypeScript Support**: Full TypeScript support with proper typing
|
|
19
|
-
- **MemberJunction Integration**: Seamlessly integrates with MemberJunction's metadata and entity systems
|
|
3
|
+
An Angular component library for searching and selecting records from any MemberJunction entity. Provides both a standalone search component and a dialog wrapper for modal usage.
|
|
20
4
|
|
|
21
5
|
## Installation
|
|
22
6
|
|
|
@@ -24,27 +8,45 @@ The `@memberjunction/ng-find-record` package simplifies the process of implement
|
|
|
24
8
|
npm install @memberjunction/ng-find-record
|
|
25
9
|
```
|
|
26
10
|
|
|
11
|
+
## Overview
|
|
12
|
+
|
|
13
|
+
The Find Record package provides a reusable search interface that works with any entity registered in the MemberJunction metadata system. It uses debounced search input with the RunView API and displays results in a Kendo UI grid for easy selection.
|
|
14
|
+
|
|
15
|
+
```mermaid
|
|
16
|
+
flowchart LR
|
|
17
|
+
subgraph Input["User Input"]
|
|
18
|
+
A["Search Text"] --> B["Debounce (300ms)"]
|
|
19
|
+
end
|
|
20
|
+
subgraph API["MJ Data Layer"]
|
|
21
|
+
B --> C["RunView + UserSearchString"]
|
|
22
|
+
C --> D["BaseEntity Results"]
|
|
23
|
+
end
|
|
24
|
+
subgraph Display["UI Components"]
|
|
25
|
+
D --> E["Kendo Grid"]
|
|
26
|
+
E --> F["OnRecordSelected"]
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
style Input fill:#2d6a9f,stroke:#1a4971,color:#fff
|
|
30
|
+
style API fill:#7c5295,stroke:#563a6b,color:#fff
|
|
31
|
+
style Display fill:#2d8659,stroke:#1a5c3a,color:#fff
|
|
32
|
+
```
|
|
33
|
+
|
|
27
34
|
## Usage
|
|
28
35
|
|
|
29
|
-
### Import
|
|
36
|
+
### Module Import
|
|
30
37
|
|
|
31
38
|
```typescript
|
|
32
39
|
import { FindRecordModule } from '@memberjunction/ng-find-record';
|
|
33
40
|
|
|
34
41
|
@NgModule({
|
|
35
|
-
imports: [
|
|
36
|
-
FindRecordModule,
|
|
37
|
-
// other imports
|
|
38
|
-
],
|
|
39
|
-
// ...
|
|
42
|
+
imports: [FindRecordModule]
|
|
40
43
|
})
|
|
41
|
-
export class YourModule {
|
|
44
|
+
export class YourModule {}
|
|
42
45
|
```
|
|
43
46
|
|
|
44
|
-
###
|
|
47
|
+
### Standalone Search Component
|
|
45
48
|
|
|
46
49
|
```html
|
|
47
|
-
<!-- Standalone search component -->
|
|
48
50
|
<mj-find-record
|
|
49
51
|
[EntityName]="'Users'"
|
|
50
52
|
[SearchDebounceTime]="500"
|
|
@@ -52,16 +54,10 @@ export class YourModule { }
|
|
|
52
54
|
</mj-find-record>
|
|
53
55
|
```
|
|
54
56
|
|
|
55
|
-
### Dialog
|
|
57
|
+
### Dialog Wrapper
|
|
56
58
|
|
|
57
59
|
```html
|
|
58
|
-
<!-- Search component in a dialog -->
|
|
59
|
-
<button kendoButton (click)="showFindDialog = true">
|
|
60
|
-
Find User
|
|
61
|
-
</button>
|
|
62
|
-
|
|
63
60
|
<mj-find-record-dialog
|
|
64
|
-
*ngIf="showFindDialog"
|
|
65
61
|
[EntityName]="'Users'"
|
|
66
62
|
[DialogTitle]="'Find User'"
|
|
67
63
|
[DialogVisible]="showFindDialog"
|
|
@@ -70,277 +66,60 @@ export class YourModule { }
|
|
|
70
66
|
</mj-find-record-dialog>
|
|
71
67
|
```
|
|
72
68
|
|
|
73
|
-
|
|
69
|
+
## Components
|
|
74
70
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
@Component({
|
|
80
|
-
selector: 'app-user-finder',
|
|
81
|
-
template: `
|
|
82
|
-
<h3>Find User</h3>
|
|
83
|
-
<button kendoButton (click)="showDialog()">Open User Finder</button>
|
|
84
|
-
|
|
85
|
-
<div *ngIf="selectedUser">
|
|
86
|
-
<h4>Selected User:</h4>
|
|
87
|
-
<p>{{ selectedUser.Get('UserName') }}</p>
|
|
88
|
-
<p>{{ selectedUser.Get('Email') }}</p>
|
|
89
|
-
</div>
|
|
90
|
-
|
|
91
|
-
<mj-find-record-dialog
|
|
92
|
-
*ngIf="dialogVisible"
|
|
93
|
-
[EntityName]="'Users'"
|
|
94
|
-
[DisplayFields]="displayFields"
|
|
95
|
-
[DialogTitle]="'Find User'"
|
|
96
|
-
[DialogVisible]="dialogVisible"
|
|
97
|
-
[DialogWidth]="'800px'"
|
|
98
|
-
[DialogHeight]="'500px'"
|
|
99
|
-
(DialogClosed)="onDialogClosed($event)"
|
|
100
|
-
(OnRecordSelected)="onUserSelected($event)">
|
|
101
|
-
</mj-find-record-dialog>
|
|
102
|
-
`
|
|
103
|
-
})
|
|
104
|
-
export class UserFinderComponent implements OnInit {
|
|
105
|
-
dialogVisible = false;
|
|
106
|
-
selectedUser: BaseEntity | null = null;
|
|
107
|
-
displayFields: EntityFieldInfo[] = [];
|
|
108
|
-
|
|
109
|
-
constructor(private metadata: Metadata) {}
|
|
110
|
-
|
|
111
|
-
ngOnInit() {
|
|
112
|
-
// Get specific fields to display in the grid
|
|
113
|
-
const userEntity = this.metadata.EntityByName('Users');
|
|
114
|
-
if (userEntity) {
|
|
115
|
-
this.displayFields = [
|
|
116
|
-
userEntity.Fields.find(f => f.Name === 'UserName')!,
|
|
117
|
-
userEntity.Fields.find(f => f.Name === 'Email')!,
|
|
118
|
-
userEntity.Fields.find(f => f.Name === 'FirstName')!,
|
|
119
|
-
userEntity.Fields.find(f => f.Name === 'LastName')!
|
|
120
|
-
];
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
showDialog() {
|
|
125
|
-
this.dialogVisible = true;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
onUserSelected(user: BaseEntity) {
|
|
129
|
-
this.selectedUser = user;
|
|
130
|
-
console.log('Selected user:', user);
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
onDialogClosed(confirmed: boolean) {
|
|
134
|
-
this.dialogVisible = false;
|
|
135
|
-
|
|
136
|
-
if (confirmed && this.selectedUser) {
|
|
137
|
-
console.log('User selection confirmed:', this.selectedUser);
|
|
138
|
-
// Do something with the selected user
|
|
139
|
-
} else {
|
|
140
|
-
console.log('User selection cancelled');
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
```
|
|
145
|
-
|
|
146
|
-
## API Reference
|
|
71
|
+
| Component | Selector | Purpose |
|
|
72
|
+
|-----------|----------|---------|
|
|
73
|
+
| `FindRecordComponent` | `mj-find-record` | Standalone search with results grid |
|
|
74
|
+
| `FindRecordDialogComponent` | `mj-find-record-dialog` | Modal dialog wrapper around the search component |
|
|
147
75
|
|
|
148
76
|
### FindRecordComponent
|
|
149
77
|
|
|
150
|
-
Standalone component for searching and selecting records. This component provides a search input with a grid display of results.
|
|
151
|
-
|
|
152
|
-
#### Selector
|
|
153
|
-
`mj-find-record`
|
|
154
|
-
|
|
155
78
|
#### Inputs
|
|
156
79
|
|
|
157
80
|
| Property | Type | Default | Description |
|
|
158
81
|
|----------|------|---------|-------------|
|
|
159
|
-
| `EntityName` | `string` | `''` |
|
|
160
|
-
| `DisplayFields` | `EntityFieldInfo[]` | `[]` |
|
|
161
|
-
| `SearchDebounceTime` | `number` | `300` |
|
|
82
|
+
| `EntityName` | `string` | `''` | Name of the MemberJunction entity to search |
|
|
83
|
+
| `DisplayFields` | `EntityFieldInfo[]` | `[]` | Fields to display in the results grid. Defaults to fields marked as `DefaultInView`, `IsPrimaryKey`, `IsNameField`, or `IncludeInUserSearchAPI` |
|
|
84
|
+
| `SearchDebounceTime` | `number` | `300` | Debounce time in milliseconds for search input |
|
|
162
85
|
|
|
163
86
|
#### Outputs
|
|
164
87
|
|
|
165
88
|
| Event | Type | Description |
|
|
166
89
|
|-------|------|-------------|
|
|
167
|
-
| `OnRecordSelected` | `EventEmitter<BaseEntity>` | Emitted when a user selects a record from the
|
|
90
|
+
| `OnRecordSelected` | `EventEmitter<BaseEntity>` | Emitted when a user selects a record from the grid |
|
|
168
91
|
|
|
169
92
|
### FindRecordDialogComponent
|
|
170
93
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
#### Selector
|
|
174
|
-
`mj-find-record-dialog`
|
|
175
|
-
|
|
176
|
-
#### Inputs
|
|
94
|
+
Inherits all inputs from `FindRecordComponent`, plus:
|
|
177
95
|
|
|
178
96
|
| Property | Type | Default | Description |
|
|
179
97
|
|----------|------|---------|-------------|
|
|
180
|
-
| `
|
|
181
|
-
| `
|
|
182
|
-
| `
|
|
183
|
-
| `
|
|
184
|
-
| `
|
|
185
|
-
| `DialogVisible` | `boolean` | `false` | **Required.** Controls the visibility of the dialog |
|
|
186
|
-
| `SelectedRecord` | `BaseEntity \| null` | `null` | Optional. Currently selected record. Can be set to pre-select a record |
|
|
187
|
-
|
|
188
|
-
#### Outputs
|
|
98
|
+
| `DialogTitle` | `string` | `'Find Record'` | Title displayed in the dialog header |
|
|
99
|
+
| `DialogWidth` | `string` | `'700px'` | Width of the dialog |
|
|
100
|
+
| `DialogHeight` | `string` | `'450px'` | Height of the dialog |
|
|
101
|
+
| `DialogVisible` | `boolean` | `false` | Controls dialog visibility |
|
|
102
|
+
| `SelectedRecord` | `BaseEntity \| null` | `null` | Pre-selected record |
|
|
189
103
|
|
|
190
104
|
| Event | Type | Description |
|
|
191
105
|
|-------|------|-------------|
|
|
192
|
-
| `DialogClosed` | `EventEmitter<boolean>` |
|
|
193
|
-
| `OnRecordSelected` | `EventEmitter<BaseEntity>` | Emitted when a user selects a record from the search results |
|
|
106
|
+
| `DialogClosed` | `EventEmitter<boolean>` | `true` if OK was clicked, `false` if cancelled |
|
|
194
107
|
|
|
195
108
|
## Search Behavior
|
|
196
109
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
3. The search leverages MemberJunction's entity metadata to search across appropriate fields
|
|
202
|
-
4. Results are displayed in a Kendo UI grid with the specified or default fields
|
|
203
|
-
5. Users can select a record by clicking on a row in the grid
|
|
204
|
-
6. The selected record (as a `BaseEntity` instance) is emitted via the `OnRecordSelected` event
|
|
205
|
-
|
|
206
|
-
### Search Implementation Details
|
|
207
|
-
|
|
208
|
-
The component uses the following RunView configuration:
|
|
209
|
-
```typescript
|
|
210
|
-
{
|
|
211
|
-
EntityName: this.EntityName,
|
|
212
|
-
UserSearchString: searchTerm,
|
|
213
|
-
ResultType: 'entity_object' // Returns BaseEntity instances
|
|
214
|
-
}
|
|
215
|
-
```
|
|
216
|
-
|
|
217
|
-
## Styling
|
|
218
|
-
|
|
219
|
-
The component includes basic CSS styling with the following classes:
|
|
220
|
-
- `.find-textbox` - Styles the search input field
|
|
221
|
-
- `.find-button` - Styles the Find button
|
|
222
|
-
|
|
223
|
-
You can override these styles in your application's global styles or component-specific stylesheets.
|
|
110
|
+
1. User types in the search input; input is debounced (default 300ms)
|
|
111
|
+
2. A search executes via `RunView` with `UserSearchString` and `ResultType: 'entity_object'`
|
|
112
|
+
3. Results display in a Kendo UI grid with the configured display fields
|
|
113
|
+
4. Clicking a row emits the selected `BaseEntity` instance via `OnRecordSelected`
|
|
224
114
|
|
|
225
115
|
## Dependencies
|
|
226
116
|
|
|
227
|
-
|
|
228
|
-
-
|
|
229
|
-
-
|
|
230
|
-
- `@
|
|
231
|
-
- `@memberjunction/ng-container-directives`: ^2.43.0 - Angular container directives
|
|
232
|
-
- `@memberjunction/ng-shared`: ^2.43.0 - Shared Angular utilities
|
|
233
|
-
- `rxjs`: ^7.8.1 - Reactive programming support for debouncing and search handling
|
|
234
|
-
- `tslib`: ^2.3.0 - TypeScript runtime library
|
|
235
|
-
|
|
236
|
-
### Peer Dependencies (must be installed in your application)
|
|
237
|
-
- `@angular/common`: 18.0.2
|
|
238
|
-
- `@angular/core`: 18.0.2
|
|
239
|
-
- `@angular/forms`: 18.0.2
|
|
240
|
-
- `@angular/router`: 18.0.2
|
|
241
|
-
- `@progress/kendo-angular-grid`: 16.2.0
|
|
242
|
-
- `@progress/kendo-angular-buttons`: 16.2.0
|
|
243
|
-
- `@progress/kendo-angular-inputs`: 16.2.0
|
|
244
|
-
- `@progress/kendo-angular-dialog`: 16.2.0
|
|
245
|
-
- `@progress/kendo-angular-listbox`: 16.2.0
|
|
246
|
-
|
|
247
|
-
## Integration with MemberJunction
|
|
248
|
-
|
|
249
|
-
This package is designed to work seamlessly with the MemberJunction ecosystem:
|
|
250
|
-
|
|
251
|
-
1. **Metadata Integration**: Uses MemberJunction's Metadata class to retrieve entity information and field definitions
|
|
252
|
-
2. **Entity System**: Works with any entity registered in the MemberJunction metadata system
|
|
253
|
-
3. **RunView API**: Leverages the powerful RunView API for searching, which respects entity permissions and field-level security
|
|
254
|
-
4. **BaseEntity**: Returns actual BaseEntity instances, allowing full access to entity methods and properties
|
|
255
|
-
|
|
256
|
-
## Build and Development
|
|
257
|
-
|
|
258
|
-
This package is part of the MemberJunction monorepo. To build:
|
|
259
|
-
|
|
260
|
-
```bash
|
|
261
|
-
# From the package directory
|
|
262
|
-
npm run build
|
|
263
|
-
|
|
264
|
-
# Or from the monorepo root
|
|
265
|
-
turbo build --filter="@memberjunction/ng-find-record"
|
|
266
|
-
```
|
|
267
|
-
|
|
268
|
-
The package uses Angular's `ngc` compiler for building Angular libraries.
|
|
269
|
-
|
|
270
|
-
## Module Configuration
|
|
271
|
-
|
|
272
|
-
The package exports a `FindRecordModule` that includes both components. Import this module in your Angular application:
|
|
273
|
-
|
|
274
|
-
```typescript
|
|
275
|
-
import { FindRecordModule } from '@memberjunction/ng-find-record';
|
|
276
|
-
|
|
277
|
-
@NgModule({
|
|
278
|
-
imports: [
|
|
279
|
-
FindRecordModule,
|
|
280
|
-
// ... other imports
|
|
281
|
-
]
|
|
282
|
-
})
|
|
283
|
-
export class YourFeatureModule { }
|
|
284
|
-
```
|
|
285
|
-
|
|
286
|
-
## Advanced Usage
|
|
287
|
-
|
|
288
|
-
### Custom Field Selection
|
|
289
|
-
|
|
290
|
-
You can programmatically select which fields to display based on your requirements:
|
|
291
|
-
|
|
292
|
-
```typescript
|
|
293
|
-
// Display only specific fields
|
|
294
|
-
const entity = this.metadata.EntityByName('Products');
|
|
295
|
-
this.displayFields = entity.Fields.filter(f =>
|
|
296
|
-
['Name', 'SKU', 'Price', 'Category'].includes(f.Name)
|
|
297
|
-
);
|
|
298
|
-
```
|
|
299
|
-
|
|
300
|
-
### Pre-selecting Records
|
|
301
|
-
|
|
302
|
-
You can pre-select a record in the dialog component:
|
|
303
|
-
|
|
304
|
-
```typescript
|
|
305
|
-
// Load a record and pre-select it
|
|
306
|
-
const md = new Metadata();
|
|
307
|
-
const user = await md.GetEntityObject<UserEntity>('Users');
|
|
308
|
-
await user.Load(userId);
|
|
309
|
-
this.selectedRecord = user;
|
|
310
|
-
```
|
|
311
|
-
|
|
312
|
-
### Handling Selection Events
|
|
313
|
-
|
|
314
|
-
Process selected records with full type safety:
|
|
315
|
-
|
|
316
|
-
```typescript
|
|
317
|
-
onRecordSelected(record: BaseEntity) {
|
|
318
|
-
// Cast to specific entity type if needed
|
|
319
|
-
if (record.EntityInfo.Name === 'Users') {
|
|
320
|
-
const user = record as UserEntity;
|
|
321
|
-
console.log('Selected user email:', user.Email);
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
// Or use generic BaseEntity methods
|
|
325
|
-
console.log('Selected record ID:', record.Get('ID'));
|
|
326
|
-
console.log('Selected record name:', record.Get(record.EntityInfo.NameField));
|
|
327
|
-
}
|
|
328
|
-
```
|
|
329
|
-
|
|
330
|
-
## Error Handling
|
|
331
|
-
|
|
332
|
-
The component includes built-in error handling:
|
|
333
|
-
|
|
334
|
-
- Failed searches will log errors using MemberJunction's `LogError` function
|
|
335
|
-
- Loading states provide visual feedback during searches
|
|
336
|
-
- Empty search results display a user-friendly message
|
|
337
|
-
|
|
338
|
-
## Performance Considerations
|
|
339
|
-
|
|
340
|
-
- **Debouncing**: The default 300ms debounce prevents excessive API calls during typing
|
|
341
|
-
- **Entity Objects**: Using `ResultType: 'entity_object'` in RunView ensures efficient object creation
|
|
342
|
-
- **Grid Virtualization**: The Kendo Grid component provides built-in virtualization for large result sets
|
|
117
|
+
- [@memberjunction/core](../../MJCore/README.md) -- Metadata, RunView, BaseEntity
|
|
118
|
+
- [@memberjunction/core-entities](../../MJCoreEntities/README.md) -- Entity type definitions
|
|
119
|
+
- [@memberjunction/ng-shared](../shared/README.md) -- Shared Angular utilities
|
|
120
|
+
- `@progress/kendo-angular-grid` -- Grid display
|
|
343
121
|
|
|
344
|
-
##
|
|
122
|
+
## Related Packages
|
|
345
123
|
|
|
346
|
-
|
|
124
|
+
- [@memberjunction/ng-record-selector](../record-selector/README.md) -- Dual-listbox record selection
|
|
125
|
+
- [@memberjunction/ng-generic-dialog](../generic-dialog/README.md) -- Reusable dialog component
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@memberjunction/ng-find-record",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.1.0",
|
|
4
4
|
"description": "MemberJunction: Angular Component to allow a user to find a single record in any entity",
|
|
5
5
|
"main": "./dist/public-api.js",
|
|
6
6
|
"typings": "./dist/public-api.d.ts",
|
|
@@ -30,11 +30,11 @@
|
|
|
30
30
|
"@progress/kendo-angular-listbox": "22.0.1"
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"@memberjunction/core-entities": "4.
|
|
34
|
-
"@memberjunction/global": "4.
|
|
35
|
-
"@memberjunction/core": "4.
|
|
36
|
-
"@memberjunction/ng-container-directives": "4.
|
|
37
|
-
"@memberjunction/ng-shared": "4.
|
|
33
|
+
"@memberjunction/core-entities": "4.1.0",
|
|
34
|
+
"@memberjunction/global": "4.1.0",
|
|
35
|
+
"@memberjunction/core": "4.1.0",
|
|
36
|
+
"@memberjunction/ng-container-directives": "4.1.0",
|
|
37
|
+
"@memberjunction/ng-shared": "4.1.0",
|
|
38
38
|
"rxjs": "^7.8.2",
|
|
39
39
|
"tslib": "^2.8.1"
|
|
40
40
|
},
|