@slickgrid-universal/angular-row-detail-plugin 0.0.1 → 10.0.0-beta.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/LICENSE +25 -0
- package/README.md +15 -41
- package/dist/angularSlickRowDetailView.d.ts +80 -0
- package/dist/angularSlickRowDetailView.js +307 -0
- package/dist/angularSlickRowDetailView.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/interfaces.d.ts +13 -0
- package/dist/interfaces.js +2 -0
- package/dist/interfaces.js.map +1 -0
- package/package.json +39 -7
- package/src/angularSlickRowDetailView.ts +400 -0
- package/src/index.ts +1 -0
- package/src/interfaces.ts +16 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
Copyright (c) 2020-present, Ghislain B.
|
|
2
|
+
https://github.com/ghiscoding/slickgrid-universal
|
|
3
|
+
|
|
4
|
+
and the original author of SlickGrid
|
|
5
|
+
Michael Leibman, michael{dot}leibman{at}gmail{dot}com
|
|
6
|
+
http://github.com/mleibman/slickgrid
|
|
7
|
+
|
|
8
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
9
|
+
a copy of this software and associated documentation files (the
|
|
10
|
+
"Software"), to deal in the Software without restriction, including
|
|
11
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
12
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
13
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
14
|
+
the following conditions:
|
|
15
|
+
|
|
16
|
+
The above copyright notice and this permission notice shall be
|
|
17
|
+
included in all copies or substantial portions of the Software.
|
|
18
|
+
|
|
19
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
20
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
21
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
22
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
23
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
24
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
25
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,45 +1,19 @@
|
|
|
1
|
-
|
|
1
|
+
[](https://opensource.org/licenses/MIT)
|
|
2
|
+
[](http://www.typescriptlang.org/)
|
|
3
|
+
[](https://github.com/ghiscoding/lerna-lite)
|
|
4
|
+
[](https://www.npmjs.com/package/@slickgrid-universal/angular-row-detail-plugin)
|
|
5
|
+
[](https://www.npmjs.com/package/@slickgrid-universal/angular-row-detail-plugin)
|
|
6
|
+
[](https://bundlephobia.com/result?p=@slickgrid-universal/angular-row-detail-plugin)
|
|
2
7
|
|
|
3
|
-
|
|
8
|
+
[](https://github.com/ghiscoding/slickgrid-universal/actions)
|
|
9
|
+
[](https://www.cypress.io/)
|
|
10
|
+
[](https://vitest.dev/)
|
|
11
|
+
[](https://codecov.io/gh/ghiscoding/slickgrid-universal)
|
|
4
12
|
|
|
5
|
-
|
|
13
|
+
## Angular-Slickgrid Row Detail plugin
|
|
14
|
+
#### @slickgrid-universal/angular-row-detail-plugin
|
|
6
15
|
|
|
7
|
-
|
|
16
|
+
Angular-Slickgrid Row Detail plugin
|
|
8
17
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
This package exists to:
|
|
12
|
-
1. Configure OIDC trusted publishing for the package name `@slickgrid-universal/angular-row-detail-plugin`
|
|
13
|
-
2. Enable secure, token-less publishing from CI/CD workflows
|
|
14
|
-
3. Establish provenance for packages published under this name
|
|
15
|
-
|
|
16
|
-
## What is OIDC Trusted Publishing?
|
|
17
|
-
|
|
18
|
-
OIDC trusted publishing allows package maintainers to publish packages directly from their CI/CD workflows without needing to manage npm access tokens. Instead, it uses OpenID Connect to establish trust between the CI/CD provider (like GitHub Actions) and npm.
|
|
19
|
-
|
|
20
|
-
## Setup Instructions
|
|
21
|
-
|
|
22
|
-
To properly configure OIDC trusted publishing for this package:
|
|
23
|
-
|
|
24
|
-
1. Go to [npmjs.com](https://www.npmjs.com/) and navigate to your package settings
|
|
25
|
-
2. Configure the trusted publisher (e.g., GitHub Actions)
|
|
26
|
-
3. Specify the repository and workflow that should be allowed to publish
|
|
27
|
-
4. Use the configured workflow to publish your actual package
|
|
28
|
-
|
|
29
|
-
## DO NOT USE THIS PACKAGE
|
|
30
|
-
|
|
31
|
-
This package is a placeholder for OIDC configuration only. It:
|
|
32
|
-
- Contains no executable code
|
|
33
|
-
- Provides no functionality
|
|
34
|
-
- Should not be installed as a dependency
|
|
35
|
-
- Exists only for administrative purposes
|
|
36
|
-
|
|
37
|
-
## More Information
|
|
38
|
-
|
|
39
|
-
For more details about npm's trusted publishing feature, see:
|
|
40
|
-
- [npm Trusted Publishing Documentation](https://docs.npmjs.com/generating-provenance-statements)
|
|
41
|
-
- [GitHub Actions OIDC Documentation](https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/about-security-hardening-with-openid-connect)
|
|
42
|
-
|
|
43
|
-
---
|
|
44
|
-
|
|
45
|
-
**Maintained for OIDC setup purposes only**
|
|
18
|
+
### Installation
|
|
19
|
+
Follow the instruction provided in the main [README](https://github.com/ghiscoding/slickgrid-universal#installation)
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import type { ApplicationRef, ComponentRef, Type, ViewContainerRef } from '@angular/core';
|
|
2
|
+
import { SlickEventData, type EventSubscription, type OnBeforeRowDetailToggleArgs, type OnRowBackOrOutOfViewportRangeArgs, type RowDetailViewOption, type RxJsFacade, type SelectionModel, type SlickGrid } from '@slickgrid-universal/common';
|
|
3
|
+
import type { EventPubSubService } from '@slickgrid-universal/event-pub-sub';
|
|
4
|
+
import { SlickRowDetailView as UniversalSlickRowDetailView } from '@slickgrid-universal/row-detail-view-plugin';
|
|
5
|
+
import { type AngularUtilService, type GridOption } from 'angular-slickgrid';
|
|
6
|
+
import { Observable, type Subject } from 'rxjs';
|
|
7
|
+
import type { RowDetailView } from './interfaces';
|
|
8
|
+
export interface CreatedView {
|
|
9
|
+
id: string | number;
|
|
10
|
+
dataContext: any;
|
|
11
|
+
componentRef?: ComponentRef<any>;
|
|
12
|
+
rendered?: boolean;
|
|
13
|
+
}
|
|
14
|
+
export declare class AngularSlickRowDetailView extends UniversalSlickRowDetailView {
|
|
15
|
+
protected readonly angularUtilService: AngularUtilService;
|
|
16
|
+
protected readonly appRef: ApplicationRef;
|
|
17
|
+
protected readonly eventPubSubService: EventPubSubService;
|
|
18
|
+
protected readonly gridContainerElement: HTMLDivElement;
|
|
19
|
+
protected rxjs?: RxJsFacade | undefined;
|
|
20
|
+
static readonly pluginName = "AngularSlickRowDetailView";
|
|
21
|
+
rowDetailContainer: ViewContainerRef;
|
|
22
|
+
protected _preloadComponent: Type<object> | undefined;
|
|
23
|
+
protected _preloadCompRef?: ComponentRef<any>;
|
|
24
|
+
protected _views: CreatedView[];
|
|
25
|
+
protected _viewComponent: Type<object>;
|
|
26
|
+
protected _subscriptions: EventSubscription[];
|
|
27
|
+
protected _userProcessFn: (item: any) => Promise<any> | Observable<any> | Subject<any>;
|
|
28
|
+
constructor(angularUtilService: AngularUtilService, appRef: ApplicationRef, eventPubSubService: EventPubSubService, gridContainerElement: HTMLDivElement, rxjs?: RxJsFacade | undefined);
|
|
29
|
+
get addonOptions(): RowDetailViewOption;
|
|
30
|
+
protected get datasetIdPropName(): string;
|
|
31
|
+
/** Getter for the Grid Options pulled through the Grid Object */
|
|
32
|
+
get gridOptions(): GridOption;
|
|
33
|
+
get rowDetailViewOptions(): RowDetailView | undefined;
|
|
34
|
+
addRxJsResource(rxjs: RxJsFacade): void;
|
|
35
|
+
/** Dispose of the RowDetailView Extension */
|
|
36
|
+
dispose(): void;
|
|
37
|
+
/** Dispose of all the opened Row Detail Panels Angular View Components */
|
|
38
|
+
disposeAllViewComponents(): void;
|
|
39
|
+
/** Get the instance of the SlickGrid addon (control or plugin). */
|
|
40
|
+
getAddonInstance(): AngularSlickRowDetailView | null;
|
|
41
|
+
init(grid: SlickGrid): void;
|
|
42
|
+
/**
|
|
43
|
+
* Create the plugin before the Grid creation, else it will behave oddly.
|
|
44
|
+
* Mostly because the column definitions might change after the grid creation
|
|
45
|
+
*/
|
|
46
|
+
register(rowSelectionPlugin?: SelectionModel): AngularSlickRowDetailView;
|
|
47
|
+
/** Redraw (re-render) all the expanded row detail View Components */
|
|
48
|
+
redrawAllViewComponents(forceRedraw?: boolean): void;
|
|
49
|
+
/** Redraw the necessary View Component */
|
|
50
|
+
redrawViewComponent(createdView: CreatedView): void;
|
|
51
|
+
/** Render (or re-render) the View Component (Row Detail) */
|
|
52
|
+
renderPreloadView(): void;
|
|
53
|
+
/** Render (or re-render) the View Component (Row Detail) */
|
|
54
|
+
renderViewModel(item: any): CreatedView | undefined;
|
|
55
|
+
protected disposeViewByItem(item: any, removeFromArray?: boolean): void;
|
|
56
|
+
protected disposeView(expandedView: CreatedView): CreatedView | void;
|
|
57
|
+
/**
|
|
58
|
+
* notify the onAsyncResponse with the "args.item" (required property)
|
|
59
|
+
* the plugin will then use item to populate the row detail panel with the "postTemplate"
|
|
60
|
+
* @param item
|
|
61
|
+
*/
|
|
62
|
+
protected notifyTemplate(item: any): void;
|
|
63
|
+
/**
|
|
64
|
+
* On Processing, we will notify the plugin with the new item detail once backend server call completes
|
|
65
|
+
* @param item
|
|
66
|
+
*/
|
|
67
|
+
protected onProcessing(item: any): Promise<void>;
|
|
68
|
+
/**
|
|
69
|
+
* Just before the row get expanded or collapsed we will do the following
|
|
70
|
+
* First determine if the row is expanding or collapsing,
|
|
71
|
+
* if it's expanding we will add it to our View Components reference array if we don't already have it
|
|
72
|
+
* or if it's collapsing we will remove it from our View Components reference array
|
|
73
|
+
*/
|
|
74
|
+
protected handleOnBeforeRowDetailToggle(_e: SlickEventData<OnBeforeRowDetailToggleArgs>, args: {
|
|
75
|
+
grid: SlickGrid;
|
|
76
|
+
item: any;
|
|
77
|
+
}): void;
|
|
78
|
+
/** When Row comes back to Viewport Range, we need to redraw the View */
|
|
79
|
+
protected handleOnRowBackToViewportRange(_e: SlickEventData<OnRowBackOrOutOfViewportRangeArgs>, args: OnRowBackOrOutOfViewportRangeArgs): void;
|
|
80
|
+
}
|
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
import { addToArrayWhenNotExists, castObservableToPromise, createDomElement, SlickEventData, SlickHybridSelectionModel, unsubscribeAll, } from '@slickgrid-universal/common';
|
|
2
|
+
import { SlickRowDetailView as UniversalSlickRowDetailView } from '@slickgrid-universal/row-detail-view-plugin';
|
|
3
|
+
import { Observable } from 'rxjs';
|
|
4
|
+
const ROW_DETAIL_CONTAINER_PREFIX = 'container_';
|
|
5
|
+
const PRELOAD_CONTAINER_PREFIX = 'container_loading';
|
|
6
|
+
export class AngularSlickRowDetailView extends UniversalSlickRowDetailView {
|
|
7
|
+
angularUtilService;
|
|
8
|
+
appRef;
|
|
9
|
+
eventPubSubService;
|
|
10
|
+
gridContainerElement;
|
|
11
|
+
rxjs;
|
|
12
|
+
static pluginName = 'AngularSlickRowDetailView';
|
|
13
|
+
rowDetailContainer;
|
|
14
|
+
_preloadComponent;
|
|
15
|
+
_preloadCompRef;
|
|
16
|
+
_views = [];
|
|
17
|
+
_viewComponent;
|
|
18
|
+
_subscriptions = [];
|
|
19
|
+
_userProcessFn;
|
|
20
|
+
constructor(angularUtilService, appRef, eventPubSubService, gridContainerElement, rxjs) {
|
|
21
|
+
super(eventPubSubService);
|
|
22
|
+
this.angularUtilService = angularUtilService;
|
|
23
|
+
this.appRef = appRef;
|
|
24
|
+
this.eventPubSubService = eventPubSubService;
|
|
25
|
+
this.gridContainerElement = gridContainerElement;
|
|
26
|
+
this.rxjs = rxjs;
|
|
27
|
+
}
|
|
28
|
+
get addonOptions() {
|
|
29
|
+
return this.getOptions();
|
|
30
|
+
}
|
|
31
|
+
get datasetIdPropName() {
|
|
32
|
+
return this.gridOptions.datasetIdPropertyName || 'id';
|
|
33
|
+
}
|
|
34
|
+
/** Getter for the Grid Options pulled through the Grid Object */
|
|
35
|
+
get gridOptions() {
|
|
36
|
+
return (this._grid?.getOptions() || {});
|
|
37
|
+
}
|
|
38
|
+
get rowDetailViewOptions() {
|
|
39
|
+
return this.gridOptions.rowDetailView;
|
|
40
|
+
}
|
|
41
|
+
addRxJsResource(rxjs) {
|
|
42
|
+
this.rxjs = rxjs;
|
|
43
|
+
}
|
|
44
|
+
/** Dispose of the RowDetailView Extension */
|
|
45
|
+
dispose() {
|
|
46
|
+
this.disposeAllViewComponents();
|
|
47
|
+
this._subscriptions = unsubscribeAll(this._subscriptions); // also unsubscribe all RxJS subscriptions
|
|
48
|
+
super.dispose();
|
|
49
|
+
}
|
|
50
|
+
/** Dispose of all the opened Row Detail Panels Angular View Components */
|
|
51
|
+
disposeAllViewComponents() {
|
|
52
|
+
do {
|
|
53
|
+
const view = this._views.pop();
|
|
54
|
+
if (view) {
|
|
55
|
+
this.disposeView(view);
|
|
56
|
+
}
|
|
57
|
+
} while (this._views.length > 0);
|
|
58
|
+
}
|
|
59
|
+
/** Get the instance of the SlickGrid addon (control or plugin). */
|
|
60
|
+
getAddonInstance() {
|
|
61
|
+
return this;
|
|
62
|
+
}
|
|
63
|
+
init(grid) {
|
|
64
|
+
this._grid = grid;
|
|
65
|
+
super.init(grid);
|
|
66
|
+
this.register(grid.getSelectionModel());
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Create the plugin before the Grid creation, else it will behave oddly.
|
|
70
|
+
* Mostly because the column definitions might change after the grid creation
|
|
71
|
+
*/
|
|
72
|
+
register(rowSelectionPlugin) {
|
|
73
|
+
if (typeof this.gridOptions.rowDetailView?.process === 'function') {
|
|
74
|
+
// we need to keep the user "process" method and replace it with our own execution method
|
|
75
|
+
// we do this because when we get the item detail, we need to call "onAsyncResponse.notify" for the plugin to work
|
|
76
|
+
this._userProcessFn = this.gridOptions.rowDetailView.process; // keep user's process method
|
|
77
|
+
this.addonOptions.process = (item) => this.onProcessing(item); // replace process method & run our internal one
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
throw new Error('[Angular-Slickgrid] You need to provide a "process" function for the Row Detail Extension to work properly');
|
|
81
|
+
}
|
|
82
|
+
if (this._grid && this.gridOptions?.rowDetailView) {
|
|
83
|
+
// load the Preload & RowDetail Templates (could be straight HTML or Angular View/ViewModel)
|
|
84
|
+
// when those are Angular View/ViewModel, we need to create View Component & provide the html containers to the Plugin (preTemplate/postTemplate methods)
|
|
85
|
+
if (!this.gridOptions.rowDetailView.preTemplate) {
|
|
86
|
+
this._preloadComponent = this.gridOptions.rowDetailView.preloadComponent;
|
|
87
|
+
this.addonOptions.preTemplate = () => createDomElement('div', { className: `${PRELOAD_CONTAINER_PREFIX}` });
|
|
88
|
+
}
|
|
89
|
+
if (!this.gridOptions.rowDetailView.postTemplate) {
|
|
90
|
+
this._viewComponent = this.gridOptions.rowDetailView.viewComponent;
|
|
91
|
+
this.addonOptions.postTemplate = (itemDetail) => createDomElement('div', { className: `${ROW_DETAIL_CONTAINER_PREFIX}${itemDetail[this.datasetIdPropName]}` });
|
|
92
|
+
}
|
|
93
|
+
// this also requires the Row Selection Model to be registered as well
|
|
94
|
+
if (!rowSelectionPlugin || !this._grid.getSelectionModel()) {
|
|
95
|
+
const selectionType = this.gridOptions.selectionOptions?.selectionType || 'row';
|
|
96
|
+
const selectActiveRow = this.gridOptions.selectionOptions?.selectActiveRow ?? true;
|
|
97
|
+
rowSelectionPlugin = new SlickHybridSelectionModel({ ...this.gridOptions.selectionOptions, selectionType, selectActiveRow });
|
|
98
|
+
this._grid.setSelectionModel(rowSelectionPlugin);
|
|
99
|
+
}
|
|
100
|
+
// hook all events
|
|
101
|
+
if (this._grid && this.rowDetailViewOptions) {
|
|
102
|
+
if (this.rowDetailViewOptions.onExtensionRegistered) {
|
|
103
|
+
this.rowDetailViewOptions.onExtensionRegistered(this);
|
|
104
|
+
}
|
|
105
|
+
this.eventHandler.subscribe(this.onAsyncResponse, (event, args) => {
|
|
106
|
+
if (this.rowDetailViewOptions && typeof this.rowDetailViewOptions.onAsyncResponse === 'function') {
|
|
107
|
+
this.rowDetailViewOptions.onAsyncResponse(event, args);
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
this.eventHandler.subscribe(this.onAsyncEndUpdate, (e, args) => {
|
|
111
|
+
// destroy preload if exists
|
|
112
|
+
this._preloadCompRef?.destroy();
|
|
113
|
+
// triggers after backend called "onAsyncResponse.notify()"
|
|
114
|
+
// because of the preload destroy above, we need a small delay to make sure the DOM element is ready to render the Row Detail
|
|
115
|
+
queueMicrotask(() => {
|
|
116
|
+
this.renderViewModel(args?.item);
|
|
117
|
+
if (this.rowDetailViewOptions && typeof this.rowDetailViewOptions.onAsyncEndUpdate === 'function') {
|
|
118
|
+
this.rowDetailViewOptions.onAsyncEndUpdate(e, args);
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
this.eventHandler.subscribe(this.onAfterRowDetailToggle, (e, args) => {
|
|
123
|
+
// display preload template & re-render all the other Detail Views after toggling
|
|
124
|
+
// the preload View will eventually go away once the data gets loaded after the "onAsyncEndUpdate" event
|
|
125
|
+
this.renderPreloadView();
|
|
126
|
+
if (this.rowDetailViewOptions && typeof this.rowDetailViewOptions.onAfterRowDetailToggle === 'function') {
|
|
127
|
+
this.rowDetailViewOptions.onAfterRowDetailToggle(e, args);
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
this.eventHandler.subscribe(this.onBeforeRowDetailToggle, (e, args) => {
|
|
131
|
+
// before toggling row detail, we need to create View Component if it doesn't exist
|
|
132
|
+
this.handleOnBeforeRowDetailToggle(e, args);
|
|
133
|
+
if (this.rowDetailViewOptions && typeof this.rowDetailViewOptions.onBeforeRowDetailToggle === 'function') {
|
|
134
|
+
return this.rowDetailViewOptions.onBeforeRowDetailToggle(e, args);
|
|
135
|
+
}
|
|
136
|
+
return true;
|
|
137
|
+
});
|
|
138
|
+
this.eventHandler.subscribe(this.onRowBackToViewportRange, (e, args) => {
|
|
139
|
+
// when row is back to viewport range, we will re-render the View Component(s)
|
|
140
|
+
this.handleOnRowBackToViewportRange(e, args);
|
|
141
|
+
if (this.rowDetailViewOptions && typeof this.rowDetailViewOptions.onRowBackToViewportRange === 'function') {
|
|
142
|
+
this.rowDetailViewOptions.onRowBackToViewportRange(e, args);
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
this._eventHandler.subscribe(this.onBeforeRowOutOfViewportRange, (event, args) => {
|
|
146
|
+
if (typeof this.rowDetailViewOptions?.onBeforeRowOutOfViewportRange === 'function') {
|
|
147
|
+
this.rowDetailViewOptions.onBeforeRowOutOfViewportRange(event, args);
|
|
148
|
+
}
|
|
149
|
+
this.disposeViewByItem(args.item);
|
|
150
|
+
});
|
|
151
|
+
this.eventHandler.subscribe(this.onRowOutOfViewportRange, (e, args) => {
|
|
152
|
+
if (this.rowDetailViewOptions && typeof this.rowDetailViewOptions.onRowOutOfViewportRange === 'function') {
|
|
153
|
+
this.rowDetailViewOptions.onRowOutOfViewportRange(e, args);
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
// --
|
|
157
|
+
// hook some events needed by the Plugin itself
|
|
158
|
+
// we need to redraw the open detail views if we change column position (column reorder)
|
|
159
|
+
this.eventHandler.subscribe(this._grid.onColumnsReordered, this.redrawAllViewComponents.bind(this, false));
|
|
160
|
+
// on row selection changed, we also need to redraw
|
|
161
|
+
if (this.gridOptions.enableSelection || this.gridOptions.enableCheckboxSelector) {
|
|
162
|
+
this.eventHandler.subscribe(this._grid.onSelectedRowsChanged, this.redrawAllViewComponents.bind(this, false));
|
|
163
|
+
}
|
|
164
|
+
// on sort, all row detail are collapsed so we can dispose of all the Views as well
|
|
165
|
+
this.eventHandler.subscribe(this._grid.onSort, this.disposeAllViewComponents.bind(this));
|
|
166
|
+
// redraw all Views whenever certain events are triggered
|
|
167
|
+
this._subscriptions.push(this.eventPubSubService?.subscribe(['onFilterChanged', 'onGridMenuColumnsChanged', 'onColumnPickerColumnsChanged'], this.redrawAllViewComponents.bind(this, false)), this.eventPubSubService?.subscribe(['onGridMenuClearAllFilters', 'onGridMenuClearAllSorting'], () => setTimeout(() => this.redrawAllViewComponents())));
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
return this;
|
|
171
|
+
}
|
|
172
|
+
/** Redraw (re-render) all the expanded row detail View Components */
|
|
173
|
+
redrawAllViewComponents(forceRedraw = false) {
|
|
174
|
+
this.resetRenderedRows();
|
|
175
|
+
this._views.forEach((view) => {
|
|
176
|
+
if (!view.rendered || forceRedraw) {
|
|
177
|
+
forceRedraw && view.componentRef?.destroy();
|
|
178
|
+
this.redrawViewComponent(view);
|
|
179
|
+
}
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
/** Redraw the necessary View Component */
|
|
183
|
+
redrawViewComponent(createdView) {
|
|
184
|
+
const containerElement = this.gridContainerElement.querySelector(`.${ROW_DETAIL_CONTAINER_PREFIX}${createdView.id}`);
|
|
185
|
+
if (containerElement) {
|
|
186
|
+
this.renderViewModel(createdView.dataContext);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
/** Render (or re-render) the View Component (Row Detail) */
|
|
190
|
+
renderPreloadView() {
|
|
191
|
+
const containerElement = this.gridContainerElement.querySelector(`.${PRELOAD_CONTAINER_PREFIX}`);
|
|
192
|
+
if (this._preloadComponent && containerElement) {
|
|
193
|
+
const preloadComp = this.angularUtilService.createAngularComponentAppendToDom(this._preloadComponent, containerElement, {}, { sanitizer: this._grid.sanitizeHtmlString });
|
|
194
|
+
this._preloadCompRef = preloadComp.componentRef;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
/** Render (or re-render) the View Component (Row Detail) */
|
|
198
|
+
renderViewModel(item) {
|
|
199
|
+
const containerElement = this.gridContainerElement.querySelector(`.${ROW_DETAIL_CONTAINER_PREFIX}${item[this.datasetIdPropName]}`);
|
|
200
|
+
if (this._viewComponent && containerElement) {
|
|
201
|
+
// render row detail
|
|
202
|
+
const componentOutput = this.angularUtilService.createAngularComponentAppendToDom(this._viewComponent, containerElement, {
|
|
203
|
+
model: item,
|
|
204
|
+
addon: this,
|
|
205
|
+
grid: this._grid,
|
|
206
|
+
dataView: this.dataView,
|
|
207
|
+
parentRef: this.rowDetailViewOptions?.parentRef,
|
|
208
|
+
}, {
|
|
209
|
+
sanitizer: this._grid.sanitizeHtmlString,
|
|
210
|
+
});
|
|
211
|
+
if (componentOutput?.componentRef) {
|
|
212
|
+
const viewObj = this._views.find((obj) => obj.id === item[this.datasetIdPropName]);
|
|
213
|
+
if (viewObj) {
|
|
214
|
+
viewObj.componentRef = componentOutput.componentRef;
|
|
215
|
+
viewObj.rendered = true;
|
|
216
|
+
}
|
|
217
|
+
return viewObj;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
return undefined;
|
|
221
|
+
}
|
|
222
|
+
// --
|
|
223
|
+
// protected functions
|
|
224
|
+
// ------------------
|
|
225
|
+
disposeViewByItem(item, removeFromArray = false) {
|
|
226
|
+
const foundViewIndex = this._views.findIndex((view) => view.id === item[this.datasetIdPropName]);
|
|
227
|
+
if (foundViewIndex >= 0) {
|
|
228
|
+
this.disposeView(this._views[foundViewIndex]);
|
|
229
|
+
if (removeFromArray) {
|
|
230
|
+
this._views.splice(foundViewIndex, 1);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
disposeView(expandedView) {
|
|
235
|
+
expandedView.rendered = false;
|
|
236
|
+
const compRef = expandedView?.componentRef;
|
|
237
|
+
if (compRef) {
|
|
238
|
+
this.appRef.detachView(compRef.hostView);
|
|
239
|
+
if (typeof compRef?.destroy === 'function') {
|
|
240
|
+
compRef.destroy();
|
|
241
|
+
}
|
|
242
|
+
return expandedView;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* notify the onAsyncResponse with the "args.item" (required property)
|
|
247
|
+
* the plugin will then use item to populate the row detail panel with the "postTemplate"
|
|
248
|
+
* @param item
|
|
249
|
+
*/
|
|
250
|
+
notifyTemplate(item) {
|
|
251
|
+
this.onAsyncResponse.notify({ item }, new SlickEventData(), this);
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* On Processing, we will notify the plugin with the new item detail once backend server call completes
|
|
255
|
+
* @param item
|
|
256
|
+
*/
|
|
257
|
+
async onProcessing(item) {
|
|
258
|
+
if (item && typeof this._userProcessFn === 'function') {
|
|
259
|
+
let awaitedItemDetail;
|
|
260
|
+
const userProcessFn = this._userProcessFn(item);
|
|
261
|
+
// wait for the "userProcessFn", once resolved we will save it into the "collection"
|
|
262
|
+
const response = await userProcessFn;
|
|
263
|
+
if (this.datasetIdPropName in response) {
|
|
264
|
+
awaitedItemDetail = response; // from Promise
|
|
265
|
+
}
|
|
266
|
+
else if ((response && response instanceof Observable) || response instanceof Promise) {
|
|
267
|
+
awaitedItemDetail = await castObservableToPromise(this.rxjs, response); // from Angular-http-client
|
|
268
|
+
}
|
|
269
|
+
if (!awaitedItemDetail || !(this.datasetIdPropName in awaitedItemDetail)) {
|
|
270
|
+
throw new Error('[Angular-Slickgrid] could not process the Row Detail, you must make sure that your "process" callback ' +
|
|
271
|
+
`returns an item object that has an "${this.datasetIdPropName}" property`);
|
|
272
|
+
}
|
|
273
|
+
// notify the plugin with the new item details
|
|
274
|
+
this.notifyTemplate(awaitedItemDetail || {});
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
/**
|
|
278
|
+
* Just before the row get expanded or collapsed we will do the following
|
|
279
|
+
* First determine if the row is expanding or collapsing,
|
|
280
|
+
* if it's expanding we will add it to our View Components reference array if we don't already have it
|
|
281
|
+
* or if it's collapsing we will remove it from our View Components reference array
|
|
282
|
+
*/
|
|
283
|
+
handleOnBeforeRowDetailToggle(_e, args) {
|
|
284
|
+
// expanding
|
|
285
|
+
if (args?.item?.__collapsed) {
|
|
286
|
+
// expanding row detail
|
|
287
|
+
const viewInfo = {
|
|
288
|
+
id: args.item[this.datasetIdPropName],
|
|
289
|
+
dataContext: args.item,
|
|
290
|
+
rendered: false,
|
|
291
|
+
};
|
|
292
|
+
addToArrayWhenNotExists(this._views, viewInfo, this.datasetIdPropName);
|
|
293
|
+
}
|
|
294
|
+
else {
|
|
295
|
+
// collapsing, so dispose of the View/Component
|
|
296
|
+
this.disposeViewByItem(args.item, true);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
/** When Row comes back to Viewport Range, we need to redraw the View */
|
|
300
|
+
handleOnRowBackToViewportRange(_e, args) {
|
|
301
|
+
const viewModel = this._views.find((x) => x.id === args.rowId);
|
|
302
|
+
if (viewModel && !viewModel.rendered) {
|
|
303
|
+
this.redrawViewComponent(viewModel);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
//# sourceMappingURL=angularSlickRowDetailView.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"angularSlickRowDetailView.js","sourceRoot":"","sources":["../src/angularSlickRowDetailView.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ApplicationRef, ComponentRef, Type, ViewContainerRef } from '@angular/core';\nimport {\n addToArrayWhenNotExists,\n castObservableToPromise,\n createDomElement,\n SlickEventData,\n SlickHybridSelectionModel,\n unsubscribeAll,\n type EventSubscription,\n type OnBeforeRowDetailToggleArgs,\n type OnRowBackOrOutOfViewportRangeArgs,\n type RowDetailViewOption,\n type RxJsFacade,\n type SelectionModel,\n type SlickGrid,\n} from '@slickgrid-universal/common';\nimport type { EventPubSubService } from '@slickgrid-universal/event-pub-sub';\nimport { SlickRowDetailView as UniversalSlickRowDetailView } from '@slickgrid-universal/row-detail-view-plugin';\nimport { type AngularUtilService, type GridOption } from 'angular-slickgrid';\nimport { Observable, type Subject } from 'rxjs';\nimport type { RowDetailView } from './interfaces';\n\nconst ROW_DETAIL_CONTAINER_PREFIX = 'container_';\nconst PRELOAD_CONTAINER_PREFIX = 'container_loading';\n\nexport interface CreatedView {\n id: string | number;\n dataContext: any;\n componentRef?: ComponentRef<any>;\n rendered?: boolean;\n}\n\nexport class AngularSlickRowDetailView extends UniversalSlickRowDetailView {\n static readonly pluginName = 'AngularSlickRowDetailView';\n rowDetailContainer!: ViewContainerRef;\n protected _preloadComponent: Type<object> | undefined;\n protected _preloadCompRef?: ComponentRef<any>;\n protected _views: CreatedView[] = [];\n protected _viewComponent!: Type<object>;\n protected _subscriptions: EventSubscription[] = [];\n protected _userProcessFn!: (item: any) => Promise<any> | Observable<any> | Subject<any>;\n\n constructor(\n protected readonly angularUtilService: AngularUtilService,\n protected readonly appRef: ApplicationRef,\n protected readonly eventPubSubService: EventPubSubService,\n protected readonly gridContainerElement: HTMLDivElement,\n protected rxjs?: RxJsFacade | undefined\n ) {\n super(eventPubSubService);\n }\n\n get addonOptions(): RowDetailViewOption {\n return this.getOptions();\n }\n\n protected get datasetIdPropName(): string {\n return this.gridOptions.datasetIdPropertyName || 'id';\n }\n\n /** Getter for the Grid Options pulled through the Grid Object */\n get gridOptions(): GridOption {\n return (this._grid?.getOptions() || {}) as GridOption;\n }\n\n get rowDetailViewOptions(): RowDetailView | undefined {\n return this.gridOptions.rowDetailView;\n }\n\n addRxJsResource(rxjs: RxJsFacade): void {\n this.rxjs = rxjs;\n }\n\n /** Dispose of the RowDetailView Extension */\n dispose(): void {\n this.disposeAllViewComponents();\n this._subscriptions = unsubscribeAll(this._subscriptions); // also unsubscribe all RxJS subscriptions\n super.dispose();\n }\n\n /** Dispose of all the opened Row Detail Panels Angular View Components */\n disposeAllViewComponents(): void {\n do {\n const view = this._views.pop();\n if (view) {\n this.disposeView(view);\n }\n } while (this._views.length > 0);\n }\n\n /** Get the instance of the SlickGrid addon (control or plugin). */\n getAddonInstance(): AngularSlickRowDetailView | null {\n return this;\n }\n\n init(grid: SlickGrid): void {\n this._grid = grid;\n super.init(grid);\n this.register(grid.getSelectionModel());\n }\n\n /**\n * Create the plugin before the Grid creation, else it will behave oddly.\n * Mostly because the column definitions might change after the grid creation\n */\n register(rowSelectionPlugin?: SelectionModel): AngularSlickRowDetailView {\n if (typeof this.gridOptions.rowDetailView?.process === 'function') {\n // we need to keep the user \"process\" method and replace it with our own execution method\n // we do this because when we get the item detail, we need to call \"onAsyncResponse.notify\" for the plugin to work\n this._userProcessFn = this.gridOptions.rowDetailView.process as (item: any) => Promise<any>; // keep user's process method\n this.addonOptions.process = (item) => this.onProcessing(item); // replace process method & run our internal one\n } else {\n throw new Error('[Angular-Slickgrid] You need to provide a \"process\" function for the Row Detail Extension to work properly');\n }\n\n if (this._grid && this.gridOptions?.rowDetailView) {\n // load the Preload & RowDetail Templates (could be straight HTML or Angular View/ViewModel)\n // when those are Angular View/ViewModel, we need to create View Component & provide the html containers to the Plugin (preTemplate/postTemplate methods)\n if (!this.gridOptions.rowDetailView.preTemplate) {\n this._preloadComponent = this.gridOptions.rowDetailView.preloadComponent;\n this.addonOptions.preTemplate = () => createDomElement('div', { className: `${PRELOAD_CONTAINER_PREFIX}` });\n }\n if (!this.gridOptions.rowDetailView.postTemplate) {\n this._viewComponent = this.gridOptions.rowDetailView.viewComponent;\n this.addonOptions.postTemplate = (itemDetail: any) =>\n createDomElement('div', { className: `${ROW_DETAIL_CONTAINER_PREFIX}${itemDetail[this.datasetIdPropName]}` });\n }\n\n // this also requires the Row Selection Model to be registered as well\n if (!rowSelectionPlugin || !this._grid.getSelectionModel()) {\n const selectionType = this.gridOptions.selectionOptions?.selectionType || 'row';\n const selectActiveRow = this.gridOptions.selectionOptions?.selectActiveRow ?? true;\n rowSelectionPlugin = new SlickHybridSelectionModel({ ...this.gridOptions.selectionOptions, selectionType, selectActiveRow });\n this._grid.setSelectionModel(rowSelectionPlugin);\n }\n\n // hook all events\n if (this._grid && this.rowDetailViewOptions) {\n if (this.rowDetailViewOptions.onExtensionRegistered) {\n this.rowDetailViewOptions.onExtensionRegistered(this);\n }\n\n this.eventHandler.subscribe(this.onAsyncResponse, (event, args) => {\n if (this.rowDetailViewOptions && typeof this.rowDetailViewOptions.onAsyncResponse === 'function') {\n this.rowDetailViewOptions.onAsyncResponse(event, args);\n }\n });\n\n this.eventHandler.subscribe(this.onAsyncEndUpdate, (e, args) => {\n // destroy preload if exists\n this._preloadCompRef?.destroy();\n\n // triggers after backend called \"onAsyncResponse.notify()\"\n // because of the preload destroy above, we need a small delay to make sure the DOM element is ready to render the Row Detail\n queueMicrotask(() => {\n this.renderViewModel(args?.item);\n\n if (this.rowDetailViewOptions && typeof this.rowDetailViewOptions.onAsyncEndUpdate === 'function') {\n this.rowDetailViewOptions.onAsyncEndUpdate(e, args);\n }\n });\n });\n\n this.eventHandler.subscribe(\n this.onAfterRowDetailToggle,\n (e: any, args: { grid: SlickGrid; item: any; expandedRows: Array<number | string> }) => {\n // display preload template & re-render all the other Detail Views after toggling\n // the preload View will eventually go away once the data gets loaded after the \"onAsyncEndUpdate\" event\n this.renderPreloadView();\n\n if (this.rowDetailViewOptions && typeof this.rowDetailViewOptions.onAfterRowDetailToggle === 'function') {\n this.rowDetailViewOptions.onAfterRowDetailToggle(e, args);\n }\n }\n );\n\n this.eventHandler.subscribe(this.onBeforeRowDetailToggle, (e, args) => {\n // before toggling row detail, we need to create View Component if it doesn't exist\n this.handleOnBeforeRowDetailToggle(e, args);\n\n if (this.rowDetailViewOptions && typeof this.rowDetailViewOptions.onBeforeRowDetailToggle === 'function') {\n return this.rowDetailViewOptions.onBeforeRowDetailToggle(e, args);\n }\n return true;\n });\n\n this.eventHandler.subscribe(this.onRowBackToViewportRange, (e, args) => {\n // when row is back to viewport range, we will re-render the View Component(s)\n this.handleOnRowBackToViewportRange(e, args);\n\n if (this.rowDetailViewOptions && typeof this.rowDetailViewOptions.onRowBackToViewportRange === 'function') {\n this.rowDetailViewOptions.onRowBackToViewportRange(e, args);\n }\n });\n\n this._eventHandler.subscribe(this.onBeforeRowOutOfViewportRange, (event, args) => {\n if (typeof this.rowDetailViewOptions?.onBeforeRowOutOfViewportRange === 'function') {\n this.rowDetailViewOptions.onBeforeRowOutOfViewportRange(event, args);\n }\n this.disposeViewByItem(args.item);\n });\n\n this.eventHandler.subscribe(this.onRowOutOfViewportRange, (e, args) => {\n if (this.rowDetailViewOptions && typeof this.rowDetailViewOptions.onRowOutOfViewportRange === 'function') {\n this.rowDetailViewOptions.onRowOutOfViewportRange(e, args);\n }\n });\n\n // --\n // hook some events needed by the Plugin itself\n\n // we need to redraw the open detail views if we change column position (column reorder)\n this.eventHandler.subscribe(this._grid.onColumnsReordered, this.redrawAllViewComponents.bind(this, false));\n\n // on row selection changed, we also need to redraw\n if (this.gridOptions.enableSelection || this.gridOptions.enableCheckboxSelector) {\n this.eventHandler.subscribe(this._grid.onSelectedRowsChanged, this.redrawAllViewComponents.bind(this, false));\n }\n\n // on sort, all row detail are collapsed so we can dispose of all the Views as well\n this.eventHandler.subscribe(this._grid.onSort, this.disposeAllViewComponents.bind(this));\n\n // redraw all Views whenever certain events are triggered\n this._subscriptions.push(\n this.eventPubSubService?.subscribe(\n ['onFilterChanged', 'onGridMenuColumnsChanged', 'onColumnPickerColumnsChanged'],\n this.redrawAllViewComponents.bind(this, false)\n ),\n this.eventPubSubService?.subscribe(['onGridMenuClearAllFilters', 'onGridMenuClearAllSorting'], () =>\n setTimeout(() => this.redrawAllViewComponents())\n )\n );\n }\n }\n return this;\n }\n\n /** Redraw (re-render) all the expanded row detail View Components */\n redrawAllViewComponents(forceRedraw = false): void {\n this.resetRenderedRows();\n this._views.forEach((view) => {\n if (!view.rendered || forceRedraw) {\n forceRedraw && view.componentRef?.destroy();\n this.redrawViewComponent(view);\n }\n });\n }\n\n /** Redraw the necessary View Component */\n redrawViewComponent(createdView: CreatedView): void {\n const containerElement = this.gridContainerElement.querySelector(`.${ROW_DETAIL_CONTAINER_PREFIX}${createdView.id}`);\n if (containerElement) {\n this.renderViewModel(createdView.dataContext);\n }\n }\n\n /** Render (or re-render) the View Component (Row Detail) */\n renderPreloadView(): void {\n const containerElement = this.gridContainerElement.querySelector(`.${PRELOAD_CONTAINER_PREFIX}`);\n if (this._preloadComponent && containerElement) {\n const preloadComp = this.angularUtilService.createAngularComponentAppendToDom(\n this._preloadComponent,\n containerElement,\n {},\n { sanitizer: this._grid.sanitizeHtmlString }\n );\n this._preloadCompRef = preloadComp.componentRef;\n }\n }\n\n /** Render (or re-render) the View Component (Row Detail) */\n renderViewModel(item: any): CreatedView | undefined {\n const containerElement = this.gridContainerElement.querySelector(`.${ROW_DETAIL_CONTAINER_PREFIX}${item[this.datasetIdPropName]}`);\n if (this._viewComponent && containerElement) {\n // render row detail\n const componentOutput = this.angularUtilService.createAngularComponentAppendToDom(\n this._viewComponent,\n containerElement,\n {\n model: item,\n addon: this,\n grid: this._grid,\n dataView: this.dataView,\n parentRef: this.rowDetailViewOptions?.parentRef,\n },\n {\n sanitizer: this._grid.sanitizeHtmlString,\n }\n );\n\n if (componentOutput?.componentRef) {\n const viewObj = this._views.find((obj) => obj.id === item[this.datasetIdPropName]);\n if (viewObj) {\n viewObj.componentRef = componentOutput.componentRef;\n viewObj.rendered = true;\n }\n return viewObj;\n }\n }\n return undefined;\n }\n\n // --\n // protected functions\n // ------------------\n\n protected disposeViewByItem(item: any, removeFromArray = false): void {\n const foundViewIndex = this._views.findIndex((view: CreatedView) => view.id === item[this.datasetIdPropName]);\n if (foundViewIndex >= 0) {\n this.disposeView(this._views[foundViewIndex]);\n if (removeFromArray) {\n this._views.splice(foundViewIndex, 1);\n }\n }\n }\n\n protected disposeView(expandedView: CreatedView): CreatedView | void {\n expandedView.rendered = false;\n const compRef = expandedView?.componentRef;\n if (compRef) {\n this.appRef.detachView(compRef.hostView);\n if (typeof compRef?.destroy === 'function') {\n compRef.destroy();\n }\n return expandedView;\n }\n }\n\n /**\n * notify the onAsyncResponse with the \"args.item\" (required property)\n * the plugin will then use item to populate the row detail panel with the \"postTemplate\"\n * @param item\n */\n protected notifyTemplate(item: any): void {\n this.onAsyncResponse.notify({ item }, new SlickEventData(), this);\n }\n\n /**\n * On Processing, we will notify the plugin with the new item detail once backend server call completes\n * @param item\n */\n protected async onProcessing(item: any): Promise<void> {\n if (item && typeof this._userProcessFn === 'function') {\n let awaitedItemDetail: any;\n const userProcessFn = this._userProcessFn(item);\n\n // wait for the \"userProcessFn\", once resolved we will save it into the \"collection\"\n const response: any | any[] = await userProcessFn;\n\n if (this.datasetIdPropName in response) {\n awaitedItemDetail = response; // from Promise\n } else if ((response && response instanceof Observable) || response instanceof Promise) {\n awaitedItemDetail = await castObservableToPromise(this.rxjs as RxJsFacade, response); // from Angular-http-client\n }\n\n if (!awaitedItemDetail || !(this.datasetIdPropName in awaitedItemDetail)) {\n throw new Error(\n '[Angular-Slickgrid] could not process the Row Detail, you must make sure that your \"process\" callback ' +\n `returns an item object that has an \"${this.datasetIdPropName}\" property`\n );\n }\n\n // notify the plugin with the new item details\n this.notifyTemplate(awaitedItemDetail || {});\n }\n }\n\n /**\n * Just before the row get expanded or collapsed we will do the following\n * First determine if the row is expanding or collapsing,\n * if it's expanding we will add it to our View Components reference array if we don't already have it\n * or if it's collapsing we will remove it from our View Components reference array\n */\n protected handleOnBeforeRowDetailToggle(_e: SlickEventData<OnBeforeRowDetailToggleArgs>, args: { grid: SlickGrid; item: any }): void {\n // expanding\n if (args?.item?.__collapsed) {\n // expanding row detail\n const viewInfo: CreatedView = {\n id: args.item[this.datasetIdPropName],\n dataContext: args.item,\n rendered: false,\n };\n addToArrayWhenNotExists(this._views, viewInfo, this.datasetIdPropName);\n } else {\n // collapsing, so dispose of the View/Component\n this.disposeViewByItem(args.item, true);\n }\n }\n\n /** When Row comes back to Viewport Range, we need to redraw the View */\n protected handleOnRowBackToViewportRange(\n _e: SlickEventData<OnRowBackOrOutOfViewportRangeArgs>,\n args: OnRowBackOrOutOfViewportRangeArgs\n ): void {\n const viewModel = this._views.find((x) => x.id === args.rowId);\n if (viewModel && !viewModel.rendered) {\n this.redrawViewComponent(viewModel);\n }\n }\n}\n"]}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './angularSlickRowDetailView';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,6BAA6B,CAAC","sourcesContent":["export * from './angularSlickRowDetailView';\n"]}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Type } from '@angular/core';
|
|
2
|
+
import type { RowDetailView as UniversalRowDetailView } from '@slickgrid-universal/common';
|
|
3
|
+
export interface RowDetailView extends UniversalRowDetailView {
|
|
4
|
+
/**
|
|
5
|
+
* Optionally pass your Parent Component reference to your Child Component (row detail component).
|
|
6
|
+
* note:: If anyone finds a better way of passing the parent to the row detail extension, please reach out and/or create a PR
|
|
7
|
+
*/
|
|
8
|
+
parentRef?: any;
|
|
9
|
+
/** View Component of the preload template (typically a spinner) which shows after opening on the row detail but before the row detail is ready */
|
|
10
|
+
preloadComponent?: Type<object>;
|
|
11
|
+
/** View Component that will be loaded in the row detail after the async function completed */
|
|
12
|
+
viewComponent: Type<object>;
|
|
13
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"interfaces.js","sourceRoot":"","sources":["../src/interfaces.ts"],"names":[],"mappings":"","sourcesContent":["import type { Type } from '@angular/core';\nimport type { RowDetailView as UniversalRowDetailView } from '@slickgrid-universal/common';\n\nexport interface RowDetailView extends UniversalRowDetailView {\n /**\n * Optionally pass your Parent Component reference to your Child Component (row detail component).\n * note:: If anyone finds a better way of passing the parent to the row detail extension, please reach out and/or create a PR\n */\n parentRef?: any;\n\n /** View Component of the preload template (typically a spinner) which shows after opening on the row detail but before the row detail is ready */\n preloadComponent?: Type<object>;\n\n /** View Component that will be loaded in the row detail after the async function completed */\n viewComponent: Type<object>;\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,10 +1,42 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@slickgrid-universal/angular-row-detail-plugin",
|
|
3
|
-
"version": "0.0.
|
|
4
|
-
"description": "
|
|
5
|
-
"
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
"
|
|
9
|
-
|
|
3
|
+
"version": "10.0.0-beta.0",
|
|
4
|
+
"description": "Angular Row Detail Plugin for Angular-SlickGrid",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"default": "./dist/index.js"
|
|
11
|
+
},
|
|
12
|
+
"./package.json": "./package.json"
|
|
13
|
+
},
|
|
14
|
+
"types": "./dist/index.d.ts",
|
|
15
|
+
"publishConfig": {
|
|
16
|
+
"access": "public"
|
|
17
|
+
},
|
|
18
|
+
"files": [
|
|
19
|
+
"/dist",
|
|
20
|
+
"/src"
|
|
21
|
+
],
|
|
22
|
+
"license": "MIT",
|
|
23
|
+
"author": "Ghislain B.",
|
|
24
|
+
"homepage": "https://github.com/ghiscoding/slickgrid-universal",
|
|
25
|
+
"repository": {
|
|
26
|
+
"type": "git",
|
|
27
|
+
"url": "git+https://github.com/ghiscoding/slickgrid-universal.git",
|
|
28
|
+
"directory": "packages/angular-row-detail-plugin"
|
|
29
|
+
},
|
|
30
|
+
"bugs": {
|
|
31
|
+
"url": "https://github.com/ghiscoding/slickgrid-universal/issues"
|
|
32
|
+
},
|
|
33
|
+
"dependencies": {
|
|
34
|
+
"@ngx-translate/core": "^17.0.0",
|
|
35
|
+
"@slickgrid-universal/common": "10.0.0-beta.0",
|
|
36
|
+
"@slickgrid-universal/event-pub-sub": "10.0.0-beta.0",
|
|
37
|
+
"@slickgrid-universal/row-detail-view-plugin": "10.0.0-beta.0",
|
|
38
|
+
"angular-slickgrid": "10.0.0-beta.0",
|
|
39
|
+
"rxjs": "^7.8.2"
|
|
40
|
+
},
|
|
41
|
+
"gitHead": "9ee3b4ce76e171dd3375f1e68e232e315364600e"
|
|
10
42
|
}
|
|
@@ -0,0 +1,400 @@
|
|
|
1
|
+
import type { ApplicationRef, ComponentRef, Type, ViewContainerRef } from '@angular/core';
|
|
2
|
+
import {
|
|
3
|
+
addToArrayWhenNotExists,
|
|
4
|
+
castObservableToPromise,
|
|
5
|
+
createDomElement,
|
|
6
|
+
SlickEventData,
|
|
7
|
+
SlickHybridSelectionModel,
|
|
8
|
+
unsubscribeAll,
|
|
9
|
+
type EventSubscription,
|
|
10
|
+
type OnBeforeRowDetailToggleArgs,
|
|
11
|
+
type OnRowBackOrOutOfViewportRangeArgs,
|
|
12
|
+
type RowDetailViewOption,
|
|
13
|
+
type RxJsFacade,
|
|
14
|
+
type SelectionModel,
|
|
15
|
+
type SlickGrid,
|
|
16
|
+
} from '@slickgrid-universal/common';
|
|
17
|
+
import type { EventPubSubService } from '@slickgrid-universal/event-pub-sub';
|
|
18
|
+
import { SlickRowDetailView as UniversalSlickRowDetailView } from '@slickgrid-universal/row-detail-view-plugin';
|
|
19
|
+
import { type AngularUtilService, type GridOption } from 'angular-slickgrid';
|
|
20
|
+
import { Observable, type Subject } from 'rxjs';
|
|
21
|
+
import type { RowDetailView } from './interfaces';
|
|
22
|
+
|
|
23
|
+
const ROW_DETAIL_CONTAINER_PREFIX = 'container_';
|
|
24
|
+
const PRELOAD_CONTAINER_PREFIX = 'container_loading';
|
|
25
|
+
|
|
26
|
+
export interface CreatedView {
|
|
27
|
+
id: string | number;
|
|
28
|
+
dataContext: any;
|
|
29
|
+
componentRef?: ComponentRef<any>;
|
|
30
|
+
rendered?: boolean;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export class AngularSlickRowDetailView extends UniversalSlickRowDetailView {
|
|
34
|
+
static readonly pluginName = 'AngularSlickRowDetailView';
|
|
35
|
+
rowDetailContainer!: ViewContainerRef;
|
|
36
|
+
protected _preloadComponent: Type<object> | undefined;
|
|
37
|
+
protected _preloadCompRef?: ComponentRef<any>;
|
|
38
|
+
protected _views: CreatedView[] = [];
|
|
39
|
+
protected _viewComponent!: Type<object>;
|
|
40
|
+
protected _subscriptions: EventSubscription[] = [];
|
|
41
|
+
protected _userProcessFn!: (item: any) => Promise<any> | Observable<any> | Subject<any>;
|
|
42
|
+
|
|
43
|
+
constructor(
|
|
44
|
+
protected readonly angularUtilService: AngularUtilService,
|
|
45
|
+
protected readonly appRef: ApplicationRef,
|
|
46
|
+
protected readonly eventPubSubService: EventPubSubService,
|
|
47
|
+
protected readonly gridContainerElement: HTMLDivElement,
|
|
48
|
+
protected rxjs?: RxJsFacade | undefined
|
|
49
|
+
) {
|
|
50
|
+
super(eventPubSubService);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
get addonOptions(): RowDetailViewOption {
|
|
54
|
+
return this.getOptions();
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
protected get datasetIdPropName(): string {
|
|
58
|
+
return this.gridOptions.datasetIdPropertyName || 'id';
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** Getter for the Grid Options pulled through the Grid Object */
|
|
62
|
+
get gridOptions(): GridOption {
|
|
63
|
+
return (this._grid?.getOptions() || {}) as GridOption;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
get rowDetailViewOptions(): RowDetailView | undefined {
|
|
67
|
+
return this.gridOptions.rowDetailView;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
addRxJsResource(rxjs: RxJsFacade): void {
|
|
71
|
+
this.rxjs = rxjs;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** Dispose of the RowDetailView Extension */
|
|
75
|
+
dispose(): void {
|
|
76
|
+
this.disposeAllViewComponents();
|
|
77
|
+
this._subscriptions = unsubscribeAll(this._subscriptions); // also unsubscribe all RxJS subscriptions
|
|
78
|
+
super.dispose();
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** Dispose of all the opened Row Detail Panels Angular View Components */
|
|
82
|
+
disposeAllViewComponents(): void {
|
|
83
|
+
do {
|
|
84
|
+
const view = this._views.pop();
|
|
85
|
+
if (view) {
|
|
86
|
+
this.disposeView(view);
|
|
87
|
+
}
|
|
88
|
+
} while (this._views.length > 0);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/** Get the instance of the SlickGrid addon (control or plugin). */
|
|
92
|
+
getAddonInstance(): AngularSlickRowDetailView | null {
|
|
93
|
+
return this;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
init(grid: SlickGrid): void {
|
|
97
|
+
this._grid = grid;
|
|
98
|
+
super.init(grid);
|
|
99
|
+
this.register(grid.getSelectionModel());
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Create the plugin before the Grid creation, else it will behave oddly.
|
|
104
|
+
* Mostly because the column definitions might change after the grid creation
|
|
105
|
+
*/
|
|
106
|
+
register(rowSelectionPlugin?: SelectionModel): AngularSlickRowDetailView {
|
|
107
|
+
if (typeof this.gridOptions.rowDetailView?.process === 'function') {
|
|
108
|
+
// we need to keep the user "process" method and replace it with our own execution method
|
|
109
|
+
// we do this because when we get the item detail, we need to call "onAsyncResponse.notify" for the plugin to work
|
|
110
|
+
this._userProcessFn = this.gridOptions.rowDetailView.process as (item: any) => Promise<any>; // keep user's process method
|
|
111
|
+
this.addonOptions.process = (item) => this.onProcessing(item); // replace process method & run our internal one
|
|
112
|
+
} else {
|
|
113
|
+
throw new Error('[Angular-Slickgrid] You need to provide a "process" function for the Row Detail Extension to work properly');
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (this._grid && this.gridOptions?.rowDetailView) {
|
|
117
|
+
// load the Preload & RowDetail Templates (could be straight HTML or Angular View/ViewModel)
|
|
118
|
+
// when those are Angular View/ViewModel, we need to create View Component & provide the html containers to the Plugin (preTemplate/postTemplate methods)
|
|
119
|
+
if (!this.gridOptions.rowDetailView.preTemplate) {
|
|
120
|
+
this._preloadComponent = this.gridOptions.rowDetailView.preloadComponent;
|
|
121
|
+
this.addonOptions.preTemplate = () => createDomElement('div', { className: `${PRELOAD_CONTAINER_PREFIX}` });
|
|
122
|
+
}
|
|
123
|
+
if (!this.gridOptions.rowDetailView.postTemplate) {
|
|
124
|
+
this._viewComponent = this.gridOptions.rowDetailView.viewComponent;
|
|
125
|
+
this.addonOptions.postTemplate = (itemDetail: any) =>
|
|
126
|
+
createDomElement('div', { className: `${ROW_DETAIL_CONTAINER_PREFIX}${itemDetail[this.datasetIdPropName]}` });
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// this also requires the Row Selection Model to be registered as well
|
|
130
|
+
if (!rowSelectionPlugin || !this._grid.getSelectionModel()) {
|
|
131
|
+
const selectionType = this.gridOptions.selectionOptions?.selectionType || 'row';
|
|
132
|
+
const selectActiveRow = this.gridOptions.selectionOptions?.selectActiveRow ?? true;
|
|
133
|
+
rowSelectionPlugin = new SlickHybridSelectionModel({ ...this.gridOptions.selectionOptions, selectionType, selectActiveRow });
|
|
134
|
+
this._grid.setSelectionModel(rowSelectionPlugin);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// hook all events
|
|
138
|
+
if (this._grid && this.rowDetailViewOptions) {
|
|
139
|
+
if (this.rowDetailViewOptions.onExtensionRegistered) {
|
|
140
|
+
this.rowDetailViewOptions.onExtensionRegistered(this);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
this.eventHandler.subscribe(this.onAsyncResponse, (event, args) => {
|
|
144
|
+
if (this.rowDetailViewOptions && typeof this.rowDetailViewOptions.onAsyncResponse === 'function') {
|
|
145
|
+
this.rowDetailViewOptions.onAsyncResponse(event, args);
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
this.eventHandler.subscribe(this.onAsyncEndUpdate, (e, args) => {
|
|
150
|
+
// destroy preload if exists
|
|
151
|
+
this._preloadCompRef?.destroy();
|
|
152
|
+
|
|
153
|
+
// triggers after backend called "onAsyncResponse.notify()"
|
|
154
|
+
// because of the preload destroy above, we need a small delay to make sure the DOM element is ready to render the Row Detail
|
|
155
|
+
queueMicrotask(() => {
|
|
156
|
+
this.renderViewModel(args?.item);
|
|
157
|
+
|
|
158
|
+
if (this.rowDetailViewOptions && typeof this.rowDetailViewOptions.onAsyncEndUpdate === 'function') {
|
|
159
|
+
this.rowDetailViewOptions.onAsyncEndUpdate(e, args);
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
this.eventHandler.subscribe(
|
|
165
|
+
this.onAfterRowDetailToggle,
|
|
166
|
+
(e: any, args: { grid: SlickGrid; item: any; expandedRows: Array<number | string> }) => {
|
|
167
|
+
// display preload template & re-render all the other Detail Views after toggling
|
|
168
|
+
// the preload View will eventually go away once the data gets loaded after the "onAsyncEndUpdate" event
|
|
169
|
+
this.renderPreloadView();
|
|
170
|
+
|
|
171
|
+
if (this.rowDetailViewOptions && typeof this.rowDetailViewOptions.onAfterRowDetailToggle === 'function') {
|
|
172
|
+
this.rowDetailViewOptions.onAfterRowDetailToggle(e, args);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
);
|
|
176
|
+
|
|
177
|
+
this.eventHandler.subscribe(this.onBeforeRowDetailToggle, (e, args) => {
|
|
178
|
+
// before toggling row detail, we need to create View Component if it doesn't exist
|
|
179
|
+
this.handleOnBeforeRowDetailToggle(e, args);
|
|
180
|
+
|
|
181
|
+
if (this.rowDetailViewOptions && typeof this.rowDetailViewOptions.onBeforeRowDetailToggle === 'function') {
|
|
182
|
+
return this.rowDetailViewOptions.onBeforeRowDetailToggle(e, args);
|
|
183
|
+
}
|
|
184
|
+
return true;
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
this.eventHandler.subscribe(this.onRowBackToViewportRange, (e, args) => {
|
|
188
|
+
// when row is back to viewport range, we will re-render the View Component(s)
|
|
189
|
+
this.handleOnRowBackToViewportRange(e, args);
|
|
190
|
+
|
|
191
|
+
if (this.rowDetailViewOptions && typeof this.rowDetailViewOptions.onRowBackToViewportRange === 'function') {
|
|
192
|
+
this.rowDetailViewOptions.onRowBackToViewportRange(e, args);
|
|
193
|
+
}
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
this._eventHandler.subscribe(this.onBeforeRowOutOfViewportRange, (event, args) => {
|
|
197
|
+
if (typeof this.rowDetailViewOptions?.onBeforeRowOutOfViewportRange === 'function') {
|
|
198
|
+
this.rowDetailViewOptions.onBeforeRowOutOfViewportRange(event, args);
|
|
199
|
+
}
|
|
200
|
+
this.disposeViewByItem(args.item);
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
this.eventHandler.subscribe(this.onRowOutOfViewportRange, (e, args) => {
|
|
204
|
+
if (this.rowDetailViewOptions && typeof this.rowDetailViewOptions.onRowOutOfViewportRange === 'function') {
|
|
205
|
+
this.rowDetailViewOptions.onRowOutOfViewportRange(e, args);
|
|
206
|
+
}
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
// --
|
|
210
|
+
// hook some events needed by the Plugin itself
|
|
211
|
+
|
|
212
|
+
// we need to redraw the open detail views if we change column position (column reorder)
|
|
213
|
+
this.eventHandler.subscribe(this._grid.onColumnsReordered, this.redrawAllViewComponents.bind(this, false));
|
|
214
|
+
|
|
215
|
+
// on row selection changed, we also need to redraw
|
|
216
|
+
if (this.gridOptions.enableSelection || this.gridOptions.enableCheckboxSelector) {
|
|
217
|
+
this.eventHandler.subscribe(this._grid.onSelectedRowsChanged, this.redrawAllViewComponents.bind(this, false));
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// on sort, all row detail are collapsed so we can dispose of all the Views as well
|
|
221
|
+
this.eventHandler.subscribe(this._grid.onSort, this.disposeAllViewComponents.bind(this));
|
|
222
|
+
|
|
223
|
+
// redraw all Views whenever certain events are triggered
|
|
224
|
+
this._subscriptions.push(
|
|
225
|
+
this.eventPubSubService?.subscribe(
|
|
226
|
+
['onFilterChanged', 'onGridMenuColumnsChanged', 'onColumnPickerColumnsChanged'],
|
|
227
|
+
this.redrawAllViewComponents.bind(this, false)
|
|
228
|
+
),
|
|
229
|
+
this.eventPubSubService?.subscribe(['onGridMenuClearAllFilters', 'onGridMenuClearAllSorting'], () =>
|
|
230
|
+
setTimeout(() => this.redrawAllViewComponents())
|
|
231
|
+
)
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
return this;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/** Redraw (re-render) all the expanded row detail View Components */
|
|
239
|
+
redrawAllViewComponents(forceRedraw = false): void {
|
|
240
|
+
this.resetRenderedRows();
|
|
241
|
+
this._views.forEach((view) => {
|
|
242
|
+
if (!view.rendered || forceRedraw) {
|
|
243
|
+
forceRedraw && view.componentRef?.destroy();
|
|
244
|
+
this.redrawViewComponent(view);
|
|
245
|
+
}
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/** Redraw the necessary View Component */
|
|
250
|
+
redrawViewComponent(createdView: CreatedView): void {
|
|
251
|
+
const containerElement = this.gridContainerElement.querySelector(`.${ROW_DETAIL_CONTAINER_PREFIX}${createdView.id}`);
|
|
252
|
+
if (containerElement) {
|
|
253
|
+
this.renderViewModel(createdView.dataContext);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/** Render (or re-render) the View Component (Row Detail) */
|
|
258
|
+
renderPreloadView(): void {
|
|
259
|
+
const containerElement = this.gridContainerElement.querySelector(`.${PRELOAD_CONTAINER_PREFIX}`);
|
|
260
|
+
if (this._preloadComponent && containerElement) {
|
|
261
|
+
const preloadComp = this.angularUtilService.createAngularComponentAppendToDom(
|
|
262
|
+
this._preloadComponent,
|
|
263
|
+
containerElement,
|
|
264
|
+
{},
|
|
265
|
+
{ sanitizer: this._grid.sanitizeHtmlString }
|
|
266
|
+
);
|
|
267
|
+
this._preloadCompRef = preloadComp.componentRef;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/** Render (or re-render) the View Component (Row Detail) */
|
|
272
|
+
renderViewModel(item: any): CreatedView | undefined {
|
|
273
|
+
const containerElement = this.gridContainerElement.querySelector(`.${ROW_DETAIL_CONTAINER_PREFIX}${item[this.datasetIdPropName]}`);
|
|
274
|
+
if (this._viewComponent && containerElement) {
|
|
275
|
+
// render row detail
|
|
276
|
+
const componentOutput = this.angularUtilService.createAngularComponentAppendToDom(
|
|
277
|
+
this._viewComponent,
|
|
278
|
+
containerElement,
|
|
279
|
+
{
|
|
280
|
+
model: item,
|
|
281
|
+
addon: this,
|
|
282
|
+
grid: this._grid,
|
|
283
|
+
dataView: this.dataView,
|
|
284
|
+
parentRef: this.rowDetailViewOptions?.parentRef,
|
|
285
|
+
},
|
|
286
|
+
{
|
|
287
|
+
sanitizer: this._grid.sanitizeHtmlString,
|
|
288
|
+
}
|
|
289
|
+
);
|
|
290
|
+
|
|
291
|
+
if (componentOutput?.componentRef) {
|
|
292
|
+
const viewObj = this._views.find((obj) => obj.id === item[this.datasetIdPropName]);
|
|
293
|
+
if (viewObj) {
|
|
294
|
+
viewObj.componentRef = componentOutput.componentRef;
|
|
295
|
+
viewObj.rendered = true;
|
|
296
|
+
}
|
|
297
|
+
return viewObj;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
return undefined;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// --
|
|
304
|
+
// protected functions
|
|
305
|
+
// ------------------
|
|
306
|
+
|
|
307
|
+
protected disposeViewByItem(item: any, removeFromArray = false): void {
|
|
308
|
+
const foundViewIndex = this._views.findIndex((view: CreatedView) => view.id === item[this.datasetIdPropName]);
|
|
309
|
+
if (foundViewIndex >= 0) {
|
|
310
|
+
this.disposeView(this._views[foundViewIndex]);
|
|
311
|
+
if (removeFromArray) {
|
|
312
|
+
this._views.splice(foundViewIndex, 1);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
protected disposeView(expandedView: CreatedView): CreatedView | void {
|
|
318
|
+
expandedView.rendered = false;
|
|
319
|
+
const compRef = expandedView?.componentRef;
|
|
320
|
+
if (compRef) {
|
|
321
|
+
this.appRef.detachView(compRef.hostView);
|
|
322
|
+
if (typeof compRef?.destroy === 'function') {
|
|
323
|
+
compRef.destroy();
|
|
324
|
+
}
|
|
325
|
+
return expandedView;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* notify the onAsyncResponse with the "args.item" (required property)
|
|
331
|
+
* the plugin will then use item to populate the row detail panel with the "postTemplate"
|
|
332
|
+
* @param item
|
|
333
|
+
*/
|
|
334
|
+
protected notifyTemplate(item: any): void {
|
|
335
|
+
this.onAsyncResponse.notify({ item }, new SlickEventData(), this);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* On Processing, we will notify the plugin with the new item detail once backend server call completes
|
|
340
|
+
* @param item
|
|
341
|
+
*/
|
|
342
|
+
protected async onProcessing(item: any): Promise<void> {
|
|
343
|
+
if (item && typeof this._userProcessFn === 'function') {
|
|
344
|
+
let awaitedItemDetail: any;
|
|
345
|
+
const userProcessFn = this._userProcessFn(item);
|
|
346
|
+
|
|
347
|
+
// wait for the "userProcessFn", once resolved we will save it into the "collection"
|
|
348
|
+
const response: any | any[] = await userProcessFn;
|
|
349
|
+
|
|
350
|
+
if (this.datasetIdPropName in response) {
|
|
351
|
+
awaitedItemDetail = response; // from Promise
|
|
352
|
+
} else if ((response && response instanceof Observable) || response instanceof Promise) {
|
|
353
|
+
awaitedItemDetail = await castObservableToPromise(this.rxjs as RxJsFacade, response); // from Angular-http-client
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
if (!awaitedItemDetail || !(this.datasetIdPropName in awaitedItemDetail)) {
|
|
357
|
+
throw new Error(
|
|
358
|
+
'[Angular-Slickgrid] could not process the Row Detail, you must make sure that your "process" callback ' +
|
|
359
|
+
`returns an item object that has an "${this.datasetIdPropName}" property`
|
|
360
|
+
);
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
// notify the plugin with the new item details
|
|
364
|
+
this.notifyTemplate(awaitedItemDetail || {});
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
* Just before the row get expanded or collapsed we will do the following
|
|
370
|
+
* First determine if the row is expanding or collapsing,
|
|
371
|
+
* if it's expanding we will add it to our View Components reference array if we don't already have it
|
|
372
|
+
* or if it's collapsing we will remove it from our View Components reference array
|
|
373
|
+
*/
|
|
374
|
+
protected handleOnBeforeRowDetailToggle(_e: SlickEventData<OnBeforeRowDetailToggleArgs>, args: { grid: SlickGrid; item: any }): void {
|
|
375
|
+
// expanding
|
|
376
|
+
if (args?.item?.__collapsed) {
|
|
377
|
+
// expanding row detail
|
|
378
|
+
const viewInfo: CreatedView = {
|
|
379
|
+
id: args.item[this.datasetIdPropName],
|
|
380
|
+
dataContext: args.item,
|
|
381
|
+
rendered: false,
|
|
382
|
+
};
|
|
383
|
+
addToArrayWhenNotExists(this._views, viewInfo, this.datasetIdPropName);
|
|
384
|
+
} else {
|
|
385
|
+
// collapsing, so dispose of the View/Component
|
|
386
|
+
this.disposeViewByItem(args.item, true);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
/** When Row comes back to Viewport Range, we need to redraw the View */
|
|
391
|
+
protected handleOnRowBackToViewportRange(
|
|
392
|
+
_e: SlickEventData<OnRowBackOrOutOfViewportRangeArgs>,
|
|
393
|
+
args: OnRowBackOrOutOfViewportRangeArgs
|
|
394
|
+
): void {
|
|
395
|
+
const viewModel = this._views.find((x) => x.id === args.rowId);
|
|
396
|
+
if (viewModel && !viewModel.rendered) {
|
|
397
|
+
this.redrawViewComponent(viewModel);
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './angularSlickRowDetailView';
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { Type } from '@angular/core';
|
|
2
|
+
import type { RowDetailView as UniversalRowDetailView } from '@slickgrid-universal/common';
|
|
3
|
+
|
|
4
|
+
export interface RowDetailView extends UniversalRowDetailView {
|
|
5
|
+
/**
|
|
6
|
+
* Optionally pass your Parent Component reference to your Child Component (row detail component).
|
|
7
|
+
* note:: If anyone finds a better way of passing the parent to the row detail extension, please reach out and/or create a PR
|
|
8
|
+
*/
|
|
9
|
+
parentRef?: any;
|
|
10
|
+
|
|
11
|
+
/** View Component of the preload template (typically a spinner) which shows after opening on the row detail but before the row detail is ready */
|
|
12
|
+
preloadComponent?: Type<object>;
|
|
13
|
+
|
|
14
|
+
/** View Component that will be loaded in the row detail after the async function completed */
|
|
15
|
+
viewComponent: Type<object>;
|
|
16
|
+
}
|