]> git.apps.os.sepia.ceph.com Git - ceph.git/blob
c4fb5f9d1a74bc4ee6d4ee555508d1c30984452e
[ceph.git] /
1 import {
2   Component,
3   EventEmitter,
4   Input,
5   OnChanges,
6   OnInit,
7   Output,
8   ViewChild
9 } from '@angular/core';
10
11 import * as _ from 'lodash';
12
13 import { CellTemplate } from '../../enum/cell-template.enum';
14 import { CdTableColumn } from '../../models/cd-table-column';
15 import { TableComponent } from '../table/table.component';
16
17 /**
18  * Display the given data in a 2 column data table. The left column
19  * shows the 'key' attribute, the right column the 'value' attribute.
20  * The data table has the following characteristics:
21  * - No header and footer is displayed
22  * - The relation of the width for the columns 'key' and 'value' is 1:3
23  * - The 'key' column is displayed in bold text
24  */
25 @Component({
26   selector: 'cd-table-key-value',
27   templateUrl: './table-key-value.component.html',
28   styleUrls: ['./table-key-value.component.scss']
29 })
30 export class TableKeyValueComponent implements OnInit, OnChanges {
31   @ViewChild(TableComponent)
32   table: TableComponent;
33
34   @Input()
35   data: any;
36   @Input()
37   autoReload: any = 5000;
38   @Input()
39   renderObjects = false;
40   // Only used if objects are rendered
41   @Input()
42   appendParentKey = true;
43
44   columns: Array<CdTableColumn> = [];
45   tableData: {
46     key: string;
47     value: any;
48   }[];
49
50   /**
51    * The function that will be called to update the input data.
52    */
53   @Output()
54   fetchData = new EventEmitter();
55
56   constructor() {}
57
58   ngOnInit() {
59     this.columns = [
60       {
61         prop: 'key',
62         flexGrow: 1,
63         cellTransformation: CellTemplate.bold
64       },
65       {
66         prop: 'value',
67         flexGrow: 3
68       }
69     ];
70     // We need to subscribe the 'fetchData' event here and not in the
71     // HTML template, otherwise the data table will display the loading
72     // indicator infinitely if data is only bound via '[data]="xyz"'.
73     // See for 'loadingIndicator' in 'TableComponent::ngOnInit()'.
74     if (this.fetchData.observers.length > 0) {
75       this.table.fetchData.subscribe(() => {
76         // Forward event triggered by the 'cd-table' data table.
77         this.fetchData.emit();
78       });
79     }
80     this.useData();
81   }
82
83   ngOnChanges(changes) {
84     this.useData();
85   }
86
87   useData() {
88     if (!this.data) {
89       return; // Wait for data
90     }
91     this.tableData = this._makePairs(this.data);
92   }
93
94   _makePairs(data: any) {
95     let temp = [];
96     if (!data) {
97       return; // Wait for data
98     } else if (_.isArray(data)) {
99       temp = this._makePairsFromArray(data);
100     } else if (_.isObject(data)) {
101       temp = this._makePairsFromObject(data);
102     } else {
103       throw new Error('Wrong data format');
104     }
105     temp = temp.map((v) => this._convertValue(v)).filter((o) => o); // Filters out undefined
106     return this.renderObjects ? this._insertFlattenObjects(temp) : temp;
107   }
108
109   _makePairsFromArray(data: any[]) {
110     let temp = [];
111     const first = data[0];
112     if (_.isArray(first)) {
113       if (first.length === 2) {
114         temp = data.map((a) => ({
115           key: a[0],
116           value: a[1]
117         }));
118       } else {
119         throw new Error('Wrong array format: [string, any][]');
120       }
121     } else if (_.isObject(first)) {
122       if (_.has(first, 'key') && _.has(first, 'value')) {
123         temp = [...data];
124       } else {
125         temp = data.reduce(
126           (previous: any[], item) => previous.concat(this._makePairsFromObject(item)),
127           temp
128         );
129       }
130     }
131     return temp;
132   }
133
134   _makePairsFromObject(data: object) {
135     return Object.keys(data).map((k) => ({
136       key: k,
137       value: data[k]
138     }));
139   }
140
141   _insertFlattenObjects(temp: any[]) {
142     const itemsToRemoveIndexes = [];
143     const itemsToAdd = [];
144     temp.forEach((v, i) => {
145       if (_.isObject(v.value)) {
146         if (_.isEmpty(v.value)) {
147           temp[i]['value'] = '';
148         } else {
149           itemsToRemoveIndexes.push(i);
150           this._makePairs(v.value).forEach((item) => {
151             if (this.appendParentKey) {
152               item.key = v.key + ' ' + item.key;
153             }
154             itemsToAdd.push(item);
155             i++;
156           });
157         }
158       }
159     });
160
161     _.remove(temp, (item, itemIndex) => {
162       return _.includes(itemsToRemoveIndexes, itemIndex);
163     });
164     itemsToAdd.forEach((item) => {
165       temp.push(item);
166     });
167
168     return temp;
169   }
170
171   _convertValue(v: any) {
172     if (_.isArray(v.value)) {
173       v.value = v.value.map((item) => (_.isObject(item) ? JSON.stringify(item) : item)).join(', ');
174     } else if (_.isObject(v.value) && !this.renderObjects) {
175       return;
176     }
177     return v;
178   }
179 }