diff --git a/docs/en/framework/ui/angular/data-table-column-extensions.md b/docs/en/framework/ui/angular/data-table-column-extensions.md index 0365336d81..81312cdd75 100644 --- a/docs/en/framework/ui/angular/data-table-column-extensions.md +++ b/docs/en/framework/ui/angular/data-table-column-extensions.md @@ -342,4 +342,5 @@ export const identityEntityPropContributors = { ## See Also +- [Extensible Table Row Detail](extensible-table-row-detail.md) - [Customizing Application Modules Guide](../../architecture/modularity/extending/customizing-application-modules-guide.md) diff --git a/docs/en/framework/ui/angular/extensible-table-row-detail.md b/docs/en/framework/ui/angular/extensible-table-row-detail.md new file mode 100644 index 0000000000..0ae24e69e1 --- /dev/null +++ b/docs/en/framework/ui/angular/extensible-table-row-detail.md @@ -0,0 +1,189 @@ +```json +//[doc-seo] +{ + "Description": "Learn how to add expandable row details to data tables using the Extensible Table Row Detail component in ABP Framework Angular UI." +} +``` + +# Extensible Table Row Detail for Angular UI + +## Introduction + +The `` component allows you to add expandable row details to any ``. When users click the expand icon, additional content is revealed below the row. + +Extensible Table Row Detail Example + +## Quick Start + +### Step 1. Import the Component + +Import `ExtensibleTableRowDetailComponent` in your component: + +```typescript +import { + ExtensibleTableComponent, + ExtensibleTableRowDetailComponent +} from '@abp/ng.components/extensible'; + +@Component({ + // ... + imports: [ + ExtensibleTableComponent, + ExtensibleTableRowDetailComponent, + ], +}) +export class MyComponent { } +``` + +### Step 2. Add Row Detail Template + +Place `` inside `` with an `ng-template`: + +```html + + + +
+
{%{{{ row.name }}}%}
+

ID: {%{{{ row.id }}}%}

+

Status: {%{{{ row.isActive ? 'Active' : 'Inactive' }}}%}

+
+
+
+
+``` + +An expand/collapse chevron icon will automatically appear in the first column of each row. + +## API + +### ExtensibleTableRowDetailComponent + +| Input | Type | Default | Description | +|-------|------|---------|-------------| +| `rowHeight` | `string` | `number` | `'100%'` | Height of the expanded row detail area | + +### Template Context Variables + +| Variable | Type | Description | +|----------|------|-------------| +| `row` | `R` | The current row data object | +| `expanded` | `boolean` | Whether the row is currently expanded | + +## Usage Examples + +### Basic Example + +Display additional information when a row is expanded: + +```html + + + +
+ Details for: {%{{{ row.name }}}%} +
{%{{{ row | json }}}%}
+
+
+
+
+``` + +### With Custom Row Height + +Specify a fixed height for the detail area: + +```html + + +
Fixed 200px height content
+
+
+``` + +### Using Expanded State + +Apply conditional styling based on expansion state: + +```html + + +
+

This row is {%{{{ expanded ? 'expanded' : 'collapsed' }}}%}

+
+
+
+``` + +### With Badges and Localization + +```html + + +
+
+
+

{%{{{ 'MyModule::Name' | abpLocalization }}}%}

+

{%{{{ row.name }}}%}

+
+
+

{%{{{ 'MyModule::Status' | abpLocalization }}}%}

+

+ @if (row.isActive) { + {%{{{ 'AbpUi::Yes' | abpLocalization }}}%} + } @else { + {%{{{ 'AbpUi::No' | abpLocalization }}}%} + } +

+
+
+
+
+
+``` + +## Alternative: Direct Template Input + +For simpler use cases, you can use the `rowDetailTemplate` input on `` directly: + +```html + + + +
{%{{{ row.name }}}%}
+
+``` + +## Events + +### rowDetailToggle + +The `rowDetailToggle` output emits when a row is expanded or collapsed: + +```html + + + ... + + +``` + +```typescript +onRowToggle(row: MyDto) { + console.log('Row toggled:', row); +} +``` + +## See Also + +- [Data Table Column Extensions](data-table-column-extensions.md) +- [Entity Action Extensions](entity-action-extensions.md) +- [Extensions Overview](extensions-overall.md) diff --git a/docs/en/framework/ui/angular/images/row-detail-image.png b/docs/en/framework/ui/angular/images/row-detail-image.png new file mode 100644 index 0000000000..c99ff98ca0 Binary files /dev/null and b/docs/en/framework/ui/angular/images/row-detail-image.png differ diff --git a/npm/ng-packs/packages/components/extensible/src/lib/components/extensible-table/extensible-table-row-detail/extensible-table-row-detail.component.ts b/npm/ng-packs/packages/components/extensible/src/lib/components/extensible-table/extensible-table-row-detail/extensible-table-row-detail.component.ts new file mode 100644 index 0000000000..aabbd2a255 --- /dev/null +++ b/npm/ng-packs/packages/components/extensible/src/lib/components/extensible-table/extensible-table-row-detail/extensible-table-row-detail.component.ts @@ -0,0 +1,10 @@ +import { Component, contentChild, input, TemplateRef } from '@angular/core'; + +@Component({ + selector: 'abp-extensible-table-row-detail', + template: '', +}) +export class ExtensibleTableRowDetailComponent { + readonly rowHeight = input('100%'); + readonly template = contentChild(TemplateRef<{ row: R; expanded: boolean }>); +} diff --git a/npm/ng-packs/packages/components/extensible/src/lib/components/extensible-table/extensible-table-row-detail/index.ts b/npm/ng-packs/packages/components/extensible/src/lib/components/extensible-table/extensible-table-row-detail/index.ts new file mode 100644 index 0000000000..e0666787b4 --- /dev/null +++ b/npm/ng-packs/packages/components/extensible/src/lib/components/extensible-table/extensible-table-row-detail/index.ts @@ -0,0 +1 @@ +export * from './extensible-table-row-detail.component'; diff --git a/npm/ng-packs/packages/components/extensible/src/lib/components/extensible-table/extensible-table.component.html b/npm/ng-packs/packages/components/extensible/src/lib/components/extensible-table/extensible-table.component.html index 8581f6c4d4..c5373aacff 100644 --- a/npm/ng-packs/packages/components/extensible/src/lib/components/extensible-table/extensible-table.component.html +++ b/npm/ng-packs/packages/components/extensible/src/lib/components/extensible-table/extensible-table.component.html @@ -1,21 +1,30 @@ @if (isBrowser) { - -@if(selectable) { - + + @if (effectiveRowDetailTemplate) { + + + + + + + + + + + + } + @if(selectable) { + @@ -44,97 +53,67 @@ } @if (actionsTemplate || (actionList.length && hasAtLeastOnePermittedAction)) { - - - - - @if (isVisibleActions(row)) { - - } - + + + + + @if (isVisibleActions(row)) { + + } - + + } @for (prop of propList; track prop.name; let i = $index) { - - - @if (prop.tooltip) { - - {{ column.name }} - - } @else { - - {{ column.name }} - - } - - - - - @if (!row['_' + prop.name].component) { - @if (prop.type === 'datetime' || prop.type === 'date' || prop.type === 'time') { -
+ + @if (prop.tooltip) { + + {{ column.name }} + + } @else { + + {{ column.name }} + + } + + + + + @if (!row['_' + prop.name].component) { + @if (prop.type === 'datetime' || prop.type === 'date' || prop.type === 'time') { +
- } @else { -
+ } @else { +
- } - } @else { -
+ } + } @else { + - } -
+ ">
+ } -
-
+ + +
}
-} +} \ No newline at end of file diff --git a/npm/ng-packs/packages/components/extensible/src/lib/components/extensible-table/extensible-table.component.ts b/npm/ng-packs/packages/components/extensible/src/lib/components/extensible-table/extensible-table.component.ts index c70d2b39e7..75253b1f02 100644 --- a/npm/ng-packs/packages/components/extensible/src/lib/components/extensible-table/extensible-table.component.ts +++ b/npm/ng-packs/packages/components/extensible/src/lib/components/extensible-table/extensible-table.component.ts @@ -4,6 +4,7 @@ import { ChangeDetectorRef, Component, computed, + ContentChild, EventEmitter, inject, Injector, @@ -17,13 +18,14 @@ import { SimpleChanges, TemplateRef, TrackByFunction, + ViewChild, } from '@angular/core'; import { AsyncPipe, isPlatformBrowser, NgComponentOutlet, NgTemplateOutlet } from '@angular/common'; import { Observable, filter, map, Subject, debounceTime, distinctUntilChanged } from 'rxjs'; import { NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; -import { NgxDatatableModule, SelectionType } from '@swimlane/ngx-datatable'; +import { NgxDatatableModule, SelectionType, DatatableComponent } from '@swimlane/ngx-datatable'; import { ABP, @@ -53,6 +55,8 @@ import { ROW_RECORD, } from '../../tokens/extensions.token'; import { GridActionsComponent } from '../grid-actions/grid-actions.component'; +import { ExtensibleTableRowDetailComponent } from './extensible-table-row-detail'; +import { RowDetailContext } from '../../models/row-detail'; const DEFAULT_ACTIONS_COLUMN_WIDTH = 150; @@ -75,6 +79,12 @@ const DEFAULT_ACTIONS_COLUMN_WIDTH = 150; ], templateUrl: './extensible-table.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + styles: [` + :host ::ng-deep .ngx-datatable.material .datatable-body .datatable-row-detail { + background: none; + padding: 0; + } + `], }) export class ExtensibleTableComponent implements OnChanges, AfterViewInit, OnDestroy { readonly #injector = inject(Injector); @@ -127,6 +137,23 @@ export class ExtensibleTableComponent implements OnChanges, AfterViewIn @Output() loadMore = new EventEmitter(); @Input() tableHeight: number; + @Input() rowDetailTemplate?: TemplateRef>; + @Input() rowDetailHeight: string | number = '100%'; + @Output() rowDetailToggle = new EventEmitter(); + + @ContentChild(ExtensibleTableRowDetailComponent) + rowDetailComponent?: ExtensibleTableRowDetailComponent; + + @ViewChild('table', { static: false }) table!: DatatableComponent; + + protected get effectiveRowDetailTemplate(): TemplateRef> | undefined { + return this.rowDetailComponent?.template() ?? this.rowDetailTemplate; + } + + protected get effectiveRowDetailHeight(): string | number { + return this.rowDetailComponent?.rowHeight() ?? this.rowDetailHeight; + } + hasAtLeastOnePermittedAction: boolean; readonly propList: EntityPropList; @@ -290,6 +317,13 @@ export class ExtensibleTableComponent implements OnChanges, AfterViewIn return this.tableHeight ? `${this.tableHeight}px` : 'auto'; } + toggleExpandRow(row: R): void { + if (this.table && this.table.rowDetail) { + this.table.rowDetail.toggleExpandRow(row); + } + this.rowDetailToggle.emit(row); + } + ngAfterViewInit(): void { if (!this.infiniteScroll) { this.list?.requestStatus$?.pipe(filter(status => status === 'loading')).subscribe(() => { diff --git a/npm/ng-packs/packages/components/extensible/src/lib/components/index.ts b/npm/ng-packs/packages/components/extensible/src/lib/components/index.ts index 149043089b..ee501c875d 100644 --- a/npm/ng-packs/packages/components/extensible/src/lib/components/index.ts +++ b/npm/ng-packs/packages/components/extensible/src/lib/components/index.ts @@ -2,6 +2,7 @@ export * from './abstract-actions/abstract-actions.component'; export * from './extensible-form/extensible-form.component'; export * from './extensible-form/extensible-form-prop.component'; export * from './extensible-table/extensible-table.component'; +export * from './extensible-table/extensible-table-row-detail'; export * from './date-time-picker/extensible-date-time-picker.component'; export * from './grid-actions/grid-actions.component'; export * from './page-toolbar/page-toolbar.component'; diff --git a/npm/ng-packs/packages/components/extensible/src/lib/extensible.module.ts b/npm/ng-packs/packages/components/extensible/src/lib/extensible.module.ts index 26741c2832..9f3ebd775b 100644 --- a/npm/ng-packs/packages/components/extensible/src/lib/extensible.module.ts +++ b/npm/ng-packs/packages/components/extensible/src/lib/extensible.module.ts @@ -13,6 +13,7 @@ import { ExtensibleFormComponent, ExtensibleFormPropComponent, ExtensibleTableComponent, + ExtensibleTableRowDetailComponent, GridActionsComponent, PageToolbarComponent, ExtensibleDateTimePickerComponent, @@ -32,6 +33,7 @@ const importWithExport = [ CreateInjectorPipe, ExtensibleFormComponent, ExtensibleTableComponent, + ExtensibleTableRowDetailComponent, ExtensibleFormMultiselectComponent, ]; @@ -50,4 +52,4 @@ const importWithExport = [ ], exports: [...importWithExport], }) -export class ExtensibleModule {} +export class ExtensibleModule { } diff --git a/npm/ng-packs/packages/components/extensible/src/lib/models/row-detail.ts b/npm/ng-packs/packages/components/extensible/src/lib/models/row-detail.ts new file mode 100644 index 0000000000..c344e9add7 --- /dev/null +++ b/npm/ng-packs/packages/components/extensible/src/lib/models/row-detail.ts @@ -0,0 +1,4 @@ +export interface RowDetailContext { + row: R; + expanded: boolean; +} diff --git a/npm/ng-packs/packages/components/extensible/src/public-api.ts b/npm/ng-packs/packages/components/extensible/src/public-api.ts index e5c7883738..6d53f8ba9a 100644 --- a/npm/ng-packs/packages/components/extensible/src/public-api.ts +++ b/npm/ng-packs/packages/components/extensible/src/public-api.ts @@ -2,6 +2,7 @@ export * from './lib/components/date-time-picker/extensible-date-time-picker.com export * from './lib/components/extensible-form/extensible-form-prop.component'; export * from './lib/components/extensible-form/extensible-form.component'; export * from './lib/components/extensible-table/extensible-table.component'; +export * from './lib/components/extensible-table/extensible-table-row-detail'; export * from './lib/components/grid-actions/grid-actions.component'; export * from './lib/components/page-toolbar/page-toolbar.component'; export * from './lib/components/multi-select'; @@ -68,4 +69,5 @@ export * from './lib/utils/form-props.util'; export * from './lib/utils/props.util'; export * from './lib/utils/state.util'; export * from './lib/utils/model.utils'; +export * from './lib/models/row-detail'; export * from './lib/extensible.module';