import { FormsModule } from '@angular/forms';
import { RouterModule } from '@angular/router';
-import { TabsModule } from 'ngx-bootstrap';
+import { TabsModule } from 'ngx-bootstrap/tabs';
+
import { ComponentsModule } from '../../shared/components/components.module';
import { SharedModule } from '../../shared/shared.module';
import { PerformanceCounterModule } from '../performance-counter/performance-counter.module';
CommonModule,
PerformanceCounterModule,
ComponentsModule,
- TabsModule,
+ TabsModule.forRoot(),
SharedModule,
RouterModule,
FormsModule
-<tabset>
+<tabset *ngIf="selection.hasSingleSelection">
<tab heading="Attributes (OSD map)">
<cd-table-key-value *ngIf="osd.loaded"
[data]="osd.details.osd_map">
import { TabsModule } from 'ngx-bootstrap';
import { DataTableModule } from '../../../../shared/datatable/datatable.module';
+import { CdTableSelection } from '../../../../shared/models/cd-table-selection';
import { PerformanceCounterModule } from '../../../performance-counter/performance-counter.module';
import {
OsdPerformanceHistogramComponent
beforeEach(() => {
fixture = TestBed.createComponent(OsdDetailsComponent);
component = fixture.componentInstance;
+
+ component.selection = new CdTableSelection();
+
fixture.detectChanges();
});
-import { Component, Input, OnInit } from '@angular/core';
+import { Component, Input, OnChanges } from '@angular/core';
import * as _ from 'lodash';
+import { CdTableSelection } from '../../../../shared/models/cd-table-selection';
import { OsdService } from '../osd.service';
@Component({
templateUrl: './osd-details.component.html',
styleUrls: ['./osd-details.component.scss']
})
-export class OsdDetailsComponent implements OnInit {
- osd: any;
+export class OsdDetailsComponent implements OnChanges {
+ @Input() selection: CdTableSelection;
- @Input() selected?: any[] = [];
+ osd: any;
constructor(private osdService: OsdService) {}
- ngOnInit() {
+ ngOnChanges() {
this.osd = {
loaded: false
};
- if (this.selected.length > 0) {
- this.osd = this.selected[0];
+ if (this.selection.hasSelection) {
+ this.osd = this.selection.first();
this.osd.autoRefresh = () => {
this.refresh();
};
<cd-table [data]="osds"
(fetchData)="getOsdList()"
[columns]="columns"
- [detailsComponent]="detailsComponent"
- [beforeShowDetails]="beforeShowDetails">
+ selectionType="single"
+ (updateSelection)="updateSelection($event)">
+ <cd-osd-details cdTableDetail
+ [selection]="selection">
+ </cd-osd-details>
</cd-table>
+
<ng-template #statusColor
let-value="value">
<span *ngFor="let state of value; last as last">
<span [class.text-success]="'up' === state || 'in' === state"
- [class.text-warning]="'down' === state || 'out' === state"
- >
+ [class.text-warning]="'down' === state || 'out' === state">
{{ state }}</span><span *ngIf="!last">, </span>
<!-- Has to be on the same line to prevent a space between state and comma. -->
</span>
import { HttpClientModule } from '@angular/common/http';
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
-import { TabsModule } from 'ngx-bootstrap';
+import { TabsModule } from 'ngx-bootstrap/tabs';
import { DataTableModule } from '../../../../shared/datatable/datatable.module';
import { DimlessPipe } from '../../../../shared/pipes/dimless.pipe';
imports: [
HttpClientModule,
PerformanceCounterModule,
- TabsModule,
+ TabsModule.forRoot(),
DataTableModule
],
declarations: [
export class OsdListComponent implements OnInit {
@ViewChild('statusColor') statusColor: TemplateRef<any>;
+
osds = [];
- detailsComponent = 'OsdDetailsComponent';
columns: CdTableColumn[];
+ selection = new CdTableSelection();
constructor(
private osdService: OsdService,
];
}
+ updateSelection(selection: CdTableSelection) {
+ this.selection = selection;
+ }
+
getOsdList() {
this.osdService.getList().subscribe((data: any[]) => {
this.osds = data;
-<tabset>
+<tabset *ngIf="selection.hasSingleSelection">
<tab i18n-heading
heading="Details">
<cd-table-key-value [data]="metadata"
-import { HttpClientModule } from '@angular/common/http';
-import { HttpClientTestingModule } from '@angular/common/http/testing';
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
-import { BsDropdownModule, TabsModule } from 'ngx-bootstrap';
+import { TabsModule } from 'ngx-bootstrap/tabs';
+import { CdTableSelection } from '../../../shared/models/cd-table-selection';
import { SharedModule } from '../../../shared/shared.module';
import { PerformanceCounterModule } from '../../performance-counter/performance-counter.module';
import { RgwDaemonService } from '../services/rgw-daemon.service';
let component: RgwDaemonDetailsComponent;
let fixture: ComponentFixture<RgwDaemonDetailsComponent>;
+ const fakeService = {
+ get: (id: string) => {
+ return new Promise(function(resolve, reject) {
+ return [];
+ });
+ }
+ };
+
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ RgwDaemonDetailsComponent ],
imports: [
SharedModule,
PerformanceCounterModule,
- HttpClientTestingModule,
- HttpClientModule,
- BsDropdownModule.forRoot(),
TabsModule.forRoot()
],
- providers: [ RgwDaemonService ]
- })
- .compileComponents();
+ providers: [{ provide: RgwDaemonService, useValue: fakeService }]
+ });
}));
beforeEach(() => {
fixture = TestBed.createComponent(RgwDaemonDetailsComponent);
component = fixture.componentInstance;
+
+ component.selection = new CdTableSelection();
+
fixture.detectChanges();
});
-import { Component, Input, OnInit } from '@angular/core';
+import { Component, Input, OnChanges } from '@angular/core';
import * as _ from 'lodash';
+import { CdTableSelection } from '../../../shared/models/cd-table-selection';
import { RgwDaemonService } from '../services/rgw-daemon.service';
@Component({
templateUrl: './rgw-daemon-details.component.html',
styleUrls: ['./rgw-daemon-details.component.scss']
})
-export class RgwDaemonDetailsComponent implements OnInit {
-
+export class RgwDaemonDetailsComponent implements OnChanges {
metadata: any;
serviceId = '';
- @Input() selected?: Array<any> = [];
+ @Input() selection: CdTableSelection;
- constructor(private rgwDaemonService: RgwDaemonService) { }
+ constructor(private rgwDaemonService: RgwDaemonService) {}
- ngOnInit() {
+ ngOnChanges() {
// Get the service id of the first selected row.
- if (this.selected.length > 0) {
- this.serviceId = this.selected[0].id;
+ if (this.selection.hasSelection) {
+ this.serviceId = this.selection.first().id;
}
}
if (_.isEmpty(this.serviceId)) {
return;
}
- this.rgwDaemonService.get(this.serviceId)
- .then((resp) => {
- this.metadata = resp['rgw_metadata'];
- });
+ this.rgwDaemonService.get(this.serviceId).then(resp => {
+ this.metadata = resp['rgw_metadata'];
+ });
}
}
aria-current="page">Object Gateway</li>
</ol>
</nav>
+
<cd-table [data]="daemons"
[columns]="columns"
columnMode="flex"
- [detailsComponent]="detailsComponent"
- (fetchData)="getDaemonList()"
- [beforeShowDetails]="beforeShowDetails">
+ selectionType="single"
+ (updateSelection)="updateSelection($event)"
+ (fetchData)="getDaemonList()">
+ <cd-rgw-daemon-details cdTableDetail
+ [selection]="selection">
+ </cd-rgw-daemon-details>
</cd-table>
import { HttpClientTestingModule } from '@angular/common/http/testing';
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
-import { BsDropdownModule } from 'ngx-bootstrap';
+import { TabsModule } from 'ngx-bootstrap/tabs';
import { DataTableModule } from '../../../shared/datatable/datatable.module';
+import { PerformanceCounterModule } from '../../performance-counter/performance-counter.module';
+import { RgwDaemonDetailsComponent } from '../rgw-daemon-details/rgw-daemon-details.component';
import { RgwDaemonService } from '../services/rgw-daemon.service';
import { RgwDaemonListComponent } from './rgw-daemon-list.component';
beforeEach(async(() => {
TestBed.configureTestingModule({
- declarations: [ RgwDaemonListComponent ],
+ declarations: [ RgwDaemonListComponent, RgwDaemonDetailsComponent ],
imports: [
DataTableModule,
HttpClientTestingModule,
- BsDropdownModule.forRoot(),
- HttpClientModule
+ HttpClientModule,
+ TabsModule.forRoot(),
+ PerformanceCounterModule
],
providers: [ RgwDaemonService ]
})
columns: Array<CdTableColumn> = [];
daemons: Array<object> = [];
-
- detailsComponent = 'RgwDaemonDetailsComponent';
+ selection = new CdTableSelection();
constructor(private rgwDaemonService: RgwDaemonService,
cephShortVersionPipe: CephShortVersionPipe) {
});
}
- beforeShowDetails(selection: CdTableSelection) {
- return selection.hasSingleSelection;
+ updateSelection(selection: CdTableSelection) {
+ this.selection = selection;
}
}
imports: [
CommonModule,
AuthModule,
- BsDropdownModule,
+ BsDropdownModule.forRoot(),
AppRoutingModule,
SharedModule,
RouterModule
import { RouterModule } from '@angular/router';
import { NgxDatatableModule } from '@swimlane/ngx-datatable';
+import { BsDropdownModule } from 'ngx-bootstrap/dropdown';
-import { BsDropdownModule } from 'ngx-bootstrap';
import { ComponentsModule } from '../components/components.module';
import { PipesModule } from '../pipes/pipes.module';
-import { TableDetailsDirective } from './table-details.directive';
import { TableKeyValueComponent } from './table-key-value/table-key-value.component';
import { TableComponent } from './table/table.component';
CommonModule,
NgxDatatableModule,
FormsModule,
- BsDropdownModule,
+ BsDropdownModule.forRoot(),
PipesModule,
ComponentsModule,
RouterModule
],
declarations: [
TableComponent,
- TableDetailsDirective,
TableKeyValueComponent
],
exports: [
+++ /dev/null
-import { TableDetailsDirective } from './table-details.directive';
-
-describe('TableDetailsDirective', () => {
- it('should create an instance', () => {
- const directive = new TableDetailsDirective(null);
- expect(directive).toBeTruthy();
- });
-});
+++ /dev/null
-import { Directive, Input, ViewContainerRef } from '@angular/core';
-
-@Directive({
- selector: '[cdTableDetails]'
-})
-export class TableDetailsDirective {
- @Input() selected?: any[];
-
- constructor(public viewContainerRef: ViewContainerRef) { }
-
-}
[loadingIndicator]="loadingIndicator"
[rowIdentity]="rowIdentity()"
[rowHeight]="'auto'">
- <!-- Row Detail Template -->
- <ngx-datatable-row-detail (toggle)="updateDetailView()">
- </ngx-datatable-row-detail>
</ngx-datatable>
</div>
-<ng-template cdTableDetails></ng-template>
+
+<!-- Table Details -->
+<ng-content select="[cdTableDetail]"></ng-content>
<!-- cell templates that can be accessed from outside -->
<ng-template #tableCellBoldTpl
import {
AfterContentChecked,
Component,
- ComponentFactoryResolver,
EventEmitter,
Input,
OnChanges,
import { CdTableColumn } from '../../models/cd-table-column';
import { CdTableSelection } from '../../models/cd-table-selection';
-import { TableDetailsDirective } from '../table-details.directive';
@Component({
selector: 'cd-table',
})
export class TableComponent implements AfterContentChecked, OnInit, OnChanges, OnDestroy {
@ViewChild(DatatableComponent) table: DatatableComponent;
- @ViewChild(TableDetailsDirective) detailTemplate: TableDetailsDirective;
@ViewChild('tableCellBoldTpl') tableCellBoldTpl: TemplateRef<any>;
@ViewChild('sparklineTpl') sparklineTpl: TemplateRef<any>;
@ViewChild('routerLinkTpl') routerLinkTpl: TemplateRef<any>;
@Input() sorts?: SortPropDir[];
// Method used for setting column widths.
@Input() columnMode ?= 'flex';
- // Name of the component e.g. 'TableDetailsComponent'
- @Input() detailsComponent?: string;
// Display the tool header, including reload button, pagination and search fields?
@Input() toolHeader ?= true;
// Display the table header?
@Input() footer ?= true;
// Page size to show. Set to 0 to show unlimited number of rows.
@Input() limit ?= 10;
- // An optional function that is called before the details page is show.
- // The current selection is passed as function argument. To do not display
- // the details page, return false.
- @Input() beforeShowDetails: Function;
/**
* Auto reload time in ms - per default every 5s
// Which row property is unique for a row
@Input() identifier = 'id';
+ // Allows other components to specify which type of selection they want,
+ // e.g. 'single' or 'multi'.
+ @Input() selectionType: string = undefined;
/**
* Should be a function to update the input data if undefined nothing will be triggered
*/
@Output() fetchData = new EventEmitter();
+ /**
+ * This should be defined if you need access to the selection object.
+ *
+ * Each time the table selection changes, this will be triggered and
+ * the new selection object will be sent.
+ *
+ * @memberof TableComponent
+ */
+ @Output() updateSelection = new EventEmitter();
+
/**
* Use this variable to access the selected row(s).
*/
cellTemplates: {
[key: string]: TemplateRef<any>
} = {};
- selectionType: string = undefined;
search = '';
rows = [];
loadingIndicator = true;
// table columns after the browser window has been resized.
private currentWidth: number;
- constructor(private componentFactoryResolver: ComponentFactoryResolver) { }
+ constructor() {}
ngOnInit() {
this._addTemplates();
}
return c;
});
- if (this.detailsComponent) {
- this.selectionType = 'multi';
- }
this.tableColumns = this.columns.filter(c => !c.isHidden);
if (this.autoReload) { // Also if nothing is bound to fetchData nothing will be triggered
// Force showing the loading indicator because it has been set to False in
}
}
- _addTemplates () {
+ _addTemplates() {
this.cellTemplates.bold = this.tableCellBoldTpl;
this.cellTemplates.sparkline = this.sparklineTpl;
this.cellTemplates.routerLink = this.routerLinkTpl;
onSelect() {
this.selection.update();
- this.toggleExpandRow();
- }
-
- toggleExpandRow() {
- if (this.selection.hasSelection) {
- this.table.rowDetail.toggleExpandRow(this.selection.first());
- } else {
- this.detailTemplate.viewContainerRef.clear();
- }
+ this.updateSelection.emit(_.clone(this.selection));
}
toggleColumn($event: any) {
];
}
- updateDetailView() {
- if (!this.detailsComponent) {
- return;
- }
- if (_.isFunction(this.beforeShowDetails)) {
- if (!this.beforeShowDetails(this.selection)) {
- this.detailTemplate.viewContainerRef.clear();
- return;
- }
- }
- const factories = Array.from(this.componentFactoryResolver['_factories'].keys());
- const factoryClass = <Type<any>>factories.find((x: any) => x.name === this.detailsComponent);
- this.detailTemplate.viewContainerRef.clear();
- const cmpRef = this.detailTemplate.viewContainerRef.createComponent(
- this.componentFactoryResolver.resolveComponentFactory(factoryClass)
- );
- cmpRef.instance.selected = this.selection.selected;
- }
-
updateFilter(event?) {
if (!event) {
this.search = '';
const val = this.search.toLowerCase();
const columns = this.columns;
// update the rows
- this.rows = this.data.filter(function (d) {
- return columns.filter((c) => {
- return (typeof d[c.prop] === 'string' || typeof d[c.prop] === 'number')
- && (d[c.prop] + '').toLowerCase().indexOf(val) !== -1;
- }).length > 0;
+ this.rows = this.data.filter((d) => {
+ return (
+ columns.filter(c => {
+ return (
+ (_.isString(d[c.prop]) || _.isNumber(d[c.prop])) &&
+ (d[c.prop] + '').toLowerCase().indexOf(val) !== -1
+ );
+ }).length > 0
+ );
});
// Whenever the filter changes, always go back to the first page
this.table.offset = 0;
// Return the function used to populate a row's CSS classes.
return () => {
return {
- 'clickable': !_.isUndefined(this.detailsComponent)
+ clickable: !_.isUndefined(this.selectionType)
};
};
}
this.hasMultiSelection = this.selected.length > 1;
}
+ /**
+ * Get the first selected row.
+ * @return {any | null}
+ */
first() {
return this.hasSelection ? this.selected[0] : null;
}