### File

`src/app/shared/node-viewer/node-viewer.component.ts`

### Implements

`OnInit`

### Metadata

encapsulation | `ViewEncapsulation.None`
providers | `ResourceService`
selector | `hi-node-viewer`
styleUrls | `./node-viewer.component.scss`
templateUrl | `./node-viewer.component.html`

### Index

###### Properties
Private _editable _listConfigs _mapConfigs Protected _obj _simpleConfigs clusterName columns headerHeight isLoading keyword listTable mapTable Protected node resourceName rowHeight simpleTable sorts
###### Methods
beforeDelete created edited getNameCellClass ngOnInit onCreate onDelete updateFilter
###### Inputs
loadingIndicator obj unlockable
###### Outputs
create delete update
###### Accessors
obj objString editable simpleConfigs listConfigs mapConfigs

### Constructor

Public `constructor(dialog: MatDialog, route: ActivatedRoute, resourceService: ResourceService, helper: HelperService)`
Defined in src/app/shared/node-viewer/node-viewer.component.ts:178
Parameters :
Name | Type | Optional
dialog | `MatDialog` | No
route | `ActivatedRoute` | No
resourceService | `ResourceService` | No
helper | `HelperService` | No

### Inputs

loadingIndicator
Type : `boolean`
Default value : `false`
Defined in src/app/shared/node-viewer/node-viewer.component.ts:73

obj
Type : `any`
Defined in src/app/shared/node-viewer/node-viewer.component.ts:111

unlockable
Type : `boolean`
Default value : `false`
Defined in src/app/shared/node-viewer/node-viewer.component.ts:70

### Outputs

create
Type : `EventEmitter<Node>`
Defined in src/app/shared/node-viewer/node-viewer.component.ts:64

delete
Type : `EventEmitter<Node>`
Defined in src/app/shared/node-viewer/node-viewer.component.ts:67

update
Type : `EventEmitter<Node>`
Defined in src/app/shared/node-viewer/node-viewer.component.ts:61

### Methods

beforeDelete
`beforeDelete(type, row)`
Defined in src/app/shared/node-viewer/node-viewer.component.ts:269
Parameters :
Name | Optional
type | No
row | No
Returns : `void`

created
`created(type, data, key)`
Defined in src/app/shared/node-viewer/node-viewer.component.ts:299
Parameters :
Name | Optional
type | No
data | No
key | No
Returns : `void`

edited
`edited(type, undefined, key, isDeleting)`
Defined in src/app/shared/node-viewer/node-viewer.component.ts:336
Parameters :
Name | Optional
type | No
No
key | No
isDeleting | No
Returns : `void`

getNameCellClass
`getNameCellClass(undefined)`
Defined in src/app/shared/node-viewer/node-viewer.component.ts:226
Parameters :
Name | Optional
No
Returns : `any`

ngOnInit
`ngOnInit()`
Defined in src/app/shared/node-viewer/node-viewer.component.ts:187
Returns : `void`

onCreate
`onCreate(type)`
Defined in src/app/shared/node-viewer/node-viewer.component.ts:233
Parameters :
Name | Optional
type | No
Returns : `void`

onDelete
`onDelete(type, row)`
Defined in src/app/shared/node-viewer/node-viewer.component.ts:285
Parameters :
Name | Optional
type | No
row | No
Returns : `void`

updateFilter
`updateFilter(event)`
Defined in src/app/shared/node-viewer/node-viewer.component.ts:211
Parameters :
Name | Optional
event | No
Returns : `void`

### Properties

Private _editable
Default value : `false`
Defined in src/app/shared/node-viewer/node-viewer.component.ts:75

_listConfigs
Type : `any[]`
Defined in src/app/shared/node-viewer/node-viewer.component.ts:105

_mapConfigs
Type : `any[]`
Defined in src/app/shared/node-viewer/node-viewer.component.ts:107

Protected _obj
Type : `any`
Defined in src/app/shared/node-viewer/node-viewer.component.ts:77

_simpleConfigs
Type : `any[]`
Defined in src/app/shared/node-viewer/node-viewer.component.ts:103

clusterName
Type : `string`
Defined in src/app/shared/node-viewer/node-viewer.component.ts:54

columns
Type : `object`
Default value : `{ simpleConfigs: [ { name: 'Name', editable: false, }, { name: 'Value', editable: false, }, ], listConfigs: [ { name: 'Value', editable: false, }, ], }`
Defined in src/app/shared/node-viewer/node-viewer.component.ts:84

headerHeight
Default value : `Settings.tableHeaderHeight`
Defined in src/app/shared/node-viewer/node-viewer.component.ts:80

isLoading
Default value : `true`
Defined in src/app/shared/node-viewer/node-viewer.component.ts:53

keyword
Type : `string`
Default value : `''`
Defined in src/app/shared/node-viewer/node-viewer.component.ts:83

listTable
Decorators : `@ViewChild('listTable', {static: true})`
Defined in src/app/shared/node-viewer/node-viewer.component.ts:57

mapTable
Decorators : `@ViewChild('mapTable', {static: true})`
Defined in src/app/shared/node-viewer/node-viewer.component.ts:58

Protected node
Type : `Node`
Defined in src/app/shared/node-viewer/node-viewer.component.ts:78

resourceName
Type : `string`
Defined in src/app/shared/node-viewer/node-viewer.component.ts:55

rowHeight
Default value : `Settings.tableRowHeight`
Defined in src/app/shared/node-viewer/node-viewer.component.ts:81

simpleTable
Decorators : `@ViewChild('simpleTable', {static: true})`
Defined in src/app/shared/node-viewer/node-viewer.component.ts:56

sorts
Type : `[]`
Default value : `[{ prop: 'name', dir: 'asc' }]`
Defined in src/app/shared/node-viewer/node-viewer.component.ts:82

### Accessors

obj
get `obj()`
Defined in src/app/shared/node-viewer/node-viewer.component.ts:117
set `obj(value: any)`
Defined in src/app/shared/node-viewer/node-viewer.component.ts:111
Parameters :
Name | Type | Optional
value | `any` | No
Returns : `void`

objString
get `objString()`
Defined in src/app/shared/node-viewer/node-viewer.component.ts:129
set `objString(value: string | null)`
Defined in src/app/shared/node-viewer/node-viewer.component.ts:120
Parameters :
Name | Type | Optional
value | `string | null` | No
Returns : `void`

editable
get `editable()`
Defined in src/app/shared/node-viewer/node-viewer.component.ts:137
set `editable(value: boolean)`
Defined in src/app/shared/node-viewer/node-viewer.component.ts:132
Parameters :
Name | Type | Optional
value | `boolean` | No
Returns : `void`

simpleConfigs
get `simpleConfigs()`
Defined in src/app/shared/node-viewer/node-viewer.component.ts:140

listConfigs
get `listConfigs()`
Defined in src/app/shared/node-viewer/node-viewer.component.ts:150

mapConfigs
get `mapConfigs()`
Defined in src/app/shared/node-viewer/node-viewer.component.ts:164

    import {
      Component,
      OnInit,
      Input,
      Output,
      ViewChild,
      ViewEncapsulation,
      EventEmitter,
    } from '@angular/core';
    import { ActivatedRoute } from '@angular/router';
    import { MatDialog } from '@angular/material/dialog';

    import * as _ from 'lodash';
    // import * as ace from 'ace-builds/src-noconflict/ace';
    import 'ace-builds';
    import 'ace-builds/webpack-resolver';
    import 'ace-builds/src-noconflict/mode-json';
    import 'ace-builds/src-noconflict/theme-chrome';
    import { config } from 'ace-builds';

    import { ResourceService } from '../../resource/shared/resource.service';
    import { HelperService } from '../../shared/helper.service';
    import { Node } from '../models/node.model';
    import { Settings } from '../../core/settings';
    import { InputDialogComponent } from '../dialog/input-dialog/input-dialog.component';
    import { ConfirmDialogComponent } from '../dialog/confirm-dialog/confirm-dialog.component';

    export type IdealState = {
      id: string;
      simpleFields?: { [key: string]: any };
      listFields?: { [key: string]: any };
      mapFields?: { [key: string]: any };
    };

    config.set(
      'basePath',
      'https://cdn.jsdelivr.net/npm/ace-builds@1.6.0/src-noconflict/'
    );
    config.setModuleUrl(
      'ace/mode/javascript_worker',
      'https://cdn.jsdelivr.net/npm/ace-builds@1.6.0/src-noconflict/worker-json.js'
    );
    @Component({
      selector: 'hi-node-viewer',
      templateUrl: './node-viewer.component.html',
      styleUrls: ['./node-viewer.component.scss'],
      providers: [ResourceService],
      // Since we are importing external styles in this component
      // we will not use Shadow DOM at all to make sure the styles apply
      encapsulation: ViewEncapsulation.None,
    })
    export class NodeViewerComponent implements OnInit {
      isLoading = true;
      clusterName: string;
      resourceName: string;
      @ViewChild('simpleTable', { static: true }) simpleTable;
      @ViewChild('listTable', { static: true }) listTable;
      @ViewChild('mapTable', { static: true }) mapTable;

      @Output('update')
      change: EventEmitter<Node> = new EventEmitter<Node>();

      @Output('create')
      create: EventEmitter<Node> = new EventEmitter<Node>();

      @Output('delete')
      delete: EventEmitter<Node> = new EventEmitter<Node>();

      @Input()
      unlockable = false;

      @Input()
      loadingIndicator = false;

      private _editable = false;

      protected _obj: any;
      protected node: Node;

      headerHeight = Settings.tableHeaderHeight;
      rowHeight = Settings.tableRowHeight;
      sorts = [{ prop: 'name', dir: 'asc' }];
      keyword = '';
      columns = {
        simpleConfigs: [
          {
            name: 'Name',
            editable: false,
          },
          {
            name: 'Value',
            editable: false,
          },
        ],
        listConfigs: [
          {
            name: 'Value',
            editable: false,
          },
        ],
      };

      _simpleConfigs: any[];

      _listConfigs: any[];

      _mapConfigs: any[];

      // MODE 1: use directly in components
      @Input()
      set obj(value: any) {
        if (value !== null) {
          this._obj = value;
          this.node = new Node(value);
        }
      }
      get obj(): any {
        return this._obj;
      }
      set objString(value: string | null) {
        let parsedValue = null;
        if (value && value !== null) {
          parsedValue = JSON.parse(value);
        }

        this.obj = parsedValue;
        this.node = new Node(parsedValue);
      }
      get objString(): string {
        return JSON.stringify(this.obj, null, 2);
      }
      set editable(value: boolean) {
        this._editable = value;
        this.columns.simpleConfigs[1].editable = this._editable;
        this.columns.listConfigs[0].editable = this._editable;
      }
      get editable() {
        return this._editable;
      }
      get simpleConfigs(): any[] {
        return this.node
          ? _.filter(
              this.node.simpleFields,
              (config) =>
                config.name.toLowerCase().indexOf(this.keyword) >= 0 ||
                config.value.toLowerCase().indexOf(this.keyword) >= 0
            )
          : [];
      }
      get listConfigs(): any[] {
        return this.node
          ? _.filter(
              this.node.listFields,
              (config) =>
                config.name.toLowerCase().indexOf(this.keyword) >= 0 ||
                _.some(
                  config.value as any[],
                  (subconfig) =>
                    subconfig.value.toLowerCase().indexOf(this.keyword) >= 0
                )
            )
          : [];
      }
      get mapConfigs(): any[] {
        return this.node
          ? _.filter(
              this.node.mapFields,
              (config) =>
                config.name.toLowerCase().trim().indexOf(this.keyword) >= 0 ||
                _.some(
                  config.value as any[],
                  (subconfig) =>
                    subconfig.name.toLowerCase().trim().indexOf(this.keyword) >=
                      0 || subconfig.value.toLowerCase().indexOf(this.keyword) >= 0
                )
            )
          : [];
      }

      public constructor(
        protected dialog: MatDialog,
        protected route: ActivatedRoute,
        protected resourceService: ResourceService,
        protected helper: HelperService
      ) {}

      ngOnInit() {
        // MODE 2: use in router
        if (this.route.snapshot.data.path) {
          const path = this.route.snapshot.data.path;

          // try parent data first
          this.obj = _.get(this.route.parent, `snapshot.data.${path}`);

          if (this.obj == null) {
            // try self data then
            this.obj = _.get(this.route.snapshot.data, path);
          }

          this.objString = JSON.stringify(this.obj, null, 2);
        }

        if (this.route.parent) {
          this.clusterName =
            this.route.parent.snapshot.params.name ||
            this.route.parent.snapshot.params.cluster_name;
          this.resourceName = this.route.parent.snapshot.params.resource_name;
        }
      }

      updateFilter(event) {
        this.keyword = event.target.value.toLowerCase().trim();

        // Whenever the filter changes, always go back to the first page
        if (this.simpleTable) {
          this.simpleTable.offset = 0;
        }
        if (this.listTable) {
          this.listTable.offset = 0;
        }
        if (this.mapTable) {
          this.mapTable.offset = 0;
        }
      }

      getNameCellClass({ value }): any {
        return {
          // highlight HELIX own configs
          primary: _.snakeCase(value).toUpperCase() === value,
        };
      }

      onCreate(type) {
        this.dialog
          .open(InputDialogComponent, {
            data: {
              title: `Create a new ${type} configuration`,
              message:
                "Please enter the name of the new configuration. You'll be able to add values later:",
              values: {
                name: {
                  label: 'the name of the new configuration',
                },
              },
            },
          })
          .afterClosed()
          .subscribe((result) => {
            if (result) {
              const entry = [
                {
                  name: result.name.value,
                  value: [],
                },
              ];

              const newNode: Node = new Node(null);
              if (type === 'list') {
                newNode.listFields = entry;
              } else if (type === 'map') {
                newNode.mapFields = entry;
              }

              this.create.emit(newNode);
            }
          });
      }

      beforeDelete(type, row) {
        this.dialog
          .open(ConfirmDialogComponent, {
            data: {
              title: 'Confirmation',
              message: 'Are you sure you want to delete this configuration?',
            },
          })
          .afterClosed()
          .subscribe((result) => {
            if (result) {
              this.onDelete(type, row);
            }
          });
      }

      onDelete(type, row) {
        const newNode: Node = new Node(null);

        if (type === 'simple') {
          newNode.appendSimpleField(row.name, '');
        } else if (type === 'list') {
          newNode.listFields = [{ name: row.name.trim(), value: [] }];
        } else if (type === 'map') {
          newNode.mapFields = [{ name: row.name.trim(), value: null }];
        }

        this.delete.emit(newNode);
      }

      created(type, data, key) {
        const newNode: Node = new Node(null);

        switch (type) {
          case 'simple':
            newNode.appendSimpleField(data.name.value, data.value.value);
            break;

          case 'list':
            if (key) {
              const entry = _.find(this.node.listFields, { name: key });

              entry.value.push({
                name: '',
                value: data.value.value,
              });

              newNode.listFields.push(entry);
            }
            break;

          case 'map':
            if (key) {
              const entry = _.find(this.node.mapFields, { name: key.trim() });

              _.forEach(entry.value, (item: any) => {
                newNode.appendMapField(key, item.name, item.value);
              });

              newNode.appendMapField(key, data.name.value, data.value.value);
            }
            break;
        }

        this.create.emit(newNode);
      }

      edited(type, { row, column, value }, key, isDeleting) {
        if (!isDeleting && column.name !== 'Value') {
          return;
        }

        const newNode: Node = new Node(null);

        switch (type) {
          case 'simple':
            newNode.appendSimpleField(row.name, value);
            break;

          case 'list':
            if (key) {
              const entry = _.find(this.node.listFields, { name: key });
              const index = _.findIndex(entry.value, { value: row.value });

              if (isDeleting) {
                entry.value.splice(index, 1);
              } else {
                entry.value[index].value = value;
              }

              newNode.listFields.push(entry);
            }
            break;

          case 'map':
            if (key) {
              // have to fetch all other configs under this key
              const entry = _.find(this.node.mapFields, { name: key.trim() });
              newNode.mapFields = [{ name: key.trim(), value: [] }];

              _.forEach(entry.value, (item: any) => {
                if (item.name === row.name) {
                  if (!isDeleting) {
                    newNode.appendMapField(key.trim(), item.name.trim(), value);
                  }
                } else {
                  newNode.appendMapField(key.trim(), item.name.trim(), item.value);
                }
              });
            }
            break;
        }

        const path = this?.route?.snapshot?.data?.path;
        if (path && path === 'idealState') {
          const idealState: IdealState = {
            id: this.resourceName,
          };

          // format the payload the way that helix-rest expects
          // before: { simpleFields: [{ name: 'NUM_PARTITIONS', value: 2 }] };
          // after:  { simpleFields: { NUM_PARTITIONS: 2 } };
          function appendIdealStateProperty(property: keyof Node) {
            if (Array.isArray(newNode[property]) && newNode[property].length > 0) {
              idealState[property] = {} as any;
              (newNode[property] as any[]).forEach((field) => {
                idealState[property][field.name] = field.value;
              });
            }
          }
          Object.keys(newNode).forEach((key) =>
            appendIdealStateProperty(key as keyof Node)
          );

          const observer = this.resourceService.setIdealState(
            this.clusterName,
            this.resourceName,
            idealState
          );

          if (observer) {
            this.isLoading = true;
            observer.subscribe(
              () => {
                this.helper.showSnackBar('Ideal State updated!');
              },
              (error) => {
                this.helper.showError(error);
                this.isLoading = false;
              },
              () => (this.isLoading = false)
            );
          }
        }

        this.change.emit(newNode);
      }
    }

    <!--
      ~ Licensed to the Apache Software Foundation (ASF) under one
      ~ or more contributor license agreements.  See the NOTICE file
      ~ distributed with this work for additional information
      ~ regarding copyright ownership.  The ASF licenses this file
      ~ to you under the Apache License, Version 2.0 (the
      ~ "License"); you may not use this file except in compliance
      ~ with the License.  You may obtain a copy of the License at
      ~
      ~     http://www.apache.org/licenses/LICENSE-2.0
      ~
      ~ Unless required by applicable law or agreed to in writing,
      ~ software distributed under the License is distributed on an
      ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
      ~ KIND, either express or implied.  See the License for the
      ~ specific language governing permissions and limitations
      ~ under the License.
      -->

    <section
      class="node-viewer"
      fxLayout="column"
      fxLayoutAlign="center center"
      fxLayoutGap="10px"
    >
      <mat-progress-bar
        *ngIf="loadingIndicator"
        mode="indeterminate"
      ></mat-progress-bar>
      <mat-button-toggle-group #group="matButtonToggleGroup" value="table">
        <mat-button-toggle value="table"> Table View </mat-button-toggle>
        <mat-button-toggle value="tree"> Tree View </mat-button-toggle>
        <mat-button-toggle value="json"> JSON View </mat-button-toggle>
      </mat-button-toggle-group>
      <section class="viewer" [ngSwitch]="group.value" fxFlexFill>
        <ngx-json-viewer *ngSwitchCase="'tree'" [json]="obj"></ngx-json-viewer>
        <ace-editor
          *ngSwitchCase="'json'"
          [(text)]="objString"
          mode="json"
          theme="chrome"
          [options]="{ useWorker: false }"
          style="min-height: 300px"
          #editor
        >
        </ace-editor>
        <section *ngSwitchCase="'table'">
          <!-- TODO vxu: use mat-simple-table when it's available -->

          <section fxLayout="row" fxLayoutAlign="center center">
            <span fxFlex="1 1 auto"></span>
            <mat-icon>search</mat-icon>
            <mat-form-field class="search-form-field">
              <input
                matInput
                placeholder="Type to filter the fields..."
                (keyup)="updateFilter($event)"
              />
            </mat-form-field>
            <span fxFlex="1 1 auto"></span>
            <!-- *ngIf="unlockable" -->
            <button
              mat-button
              (click)="editable = !editable"
              [matTooltip]="
                editable
                  ? 'Click to prevent further changes'
                  : 'Click to make changes'
              "
            >
              <mat-icon>{{ editable ? 'lock_open' : 'lock' }}</mat-icon>
              {{ editable ? 'Unlocked' : 'Locked' }}
            </button>
          </section>

          <mat-card>
            <mat-card-header>
              <mat-card-title>
                Simple Fields
                <span *ngIf="simpleConfigs.length == 0">is empty.</span>
                <span *ngIf="keyword" class="primary">(filtered)</span>
              </mat-card-title>
            </mat-card-header>
            <mat-card-content>
              <hi-data-table
                *ngIf="simpleConfigs.length || editable"
                [rows]="simpleConfigs"
                [sorts]="sorts"
                [columns]="columns.simpleConfigs"
                [deletable]="editable"
                [insertable]="editable"
                (update)="edited('simple', $event)"
                (create)="created('simple', $event)"
                (delete)="onDelete('simple', $event.row)"
              >
              </hi-data-table>
            </mat-card-content>
          </mat-card>

          <mat-card>
            <mat-card-header>
              <mat-card-title>
                List Fields
                <span *ngIf="listConfigs.length == 0">is empty.</span>
                <span *ngIf="keyword" class="primary">(filtered)</span>
              </mat-card-title>
            </mat-card-header>
            <mat-card-content>
              <ngx-datatable
                *ngIf="listConfigs.length || editable"
                #listTable
                class="material"
                [headerHeight]="headerHeight"
                rowHeight="auto"
                [footerHeight]="headerHeight"
                columnMode="force"
                [rows]="listConfigs"
                [sorts]="sorts"
                [limit]="10"
              >
                <ngx-datatable-column
                  *ngIf="editable"
                  [width]="40"
                  [resizeable]="false"
                  [draggable]="false"
                  [canAutoResize]="false"
                >
                  <ng-template let-row="row" ngx-datatable-cell-template>
                    <button
                      mat-icon-button
                      class="delete-button"
                      matTooltip="Click to delete"
                      (click)="beforeDelete('list', row)"
                    >
                      <mat-icon>delete_forever</mat-icon>
                    </button>
                  </ng-template>
                </ngx-datatable-column>
                <ngx-datatable-column
                  name="Name"
                  [width]="80"
                  [cellClass]="getNameCellClass"
                ></ngx-datatable-column>
                <ngx-datatable-column name="Value" [width]="400">
                  <ng-template
                    let-row="row"
                    let-value="value"
                    ngx-datatable-cell-template
                  >
                    <hi-data-table
                      [rows]="value"
                      [sorts]="sorts"
                      [columns]="columns.listConfigs"
                      [deletable]="editable"
                      [insertable]="editable"
                      (update)="edited('list', $event, row.name)"
                      (create)="created('list', $event, row.name)"
                      (delete)="edited('list', $event, row.name, true)"
                    >
                    </hi-data-table>
                  </ng-template>
                </ngx-datatable-column>
                <ngx-datatable-footer>
                  <ng-template
                    ngx-datatable-footer-template
                    let-rowCount="rowCount"
                    let-pageSize="pageSize"
                    let-curPage="curPage"
                  >
                    <section
                      class="footer"
                      fxLayout="row"
                      fxLayoutAlign="space-between center"
                    >
                      <button
                        mat-button
                        *ngIf="editable"
                        (click)="onCreate('list')"
                      >
                        <mat-icon>add</mat-icon>
                        Add new entry
                      </button>
                      <section>{{ rowCount }} total</section>
                      <section>
                        <datatable-pager
                          [pagerLeftArrowIcon]="'datatable-icon-left'"
                          [pagerRightArrowIcon]="'datatable-icon-right'"
                          [pagerPreviousIcon]="'datatable-icon-prev'"
                          [pagerNextIcon]="'datatable-icon-skip'"
                          [page]="curPage"
                          [size]="pageSize"
                          [count]="rowCount"
                          [hidden]="!(rowCount / pageSize > 1)"
                          (change)="listTable.onFooterPage($event)"
                        >
                        </datatable-pager>
                      </section>
                    </section>
                  </ng-template>
                </ngx-datatable-footer>
              </ngx-datatable>
            </mat-card-content>
          </mat-card>

          <mat-card>
            <mat-card-header>
              <mat-card-title>
                Map Fields
                <span *ngIf="mapConfigs.length == 0">is empty.</span>
                <span *ngIf="keyword" class="primary">(filtered)</span>
              </mat-card-title>
            </mat-card-header>
            <mat-card-content>
              <ngx-datatable
                *ngIf="mapConfigs.length || editable"
                #mapTable
                class="material"
                [headerHeight]="headerHeight"
                rowHeight="auto"
                [footerHeight]="headerHeight"
                columnMode="force"
                [rows]="mapConfigs"
                [sorts]="sorts"
                [limit]="10"
              >
                <ngx-datatable-column
                  *ngIf="editable"
                  [width]="40"
                  [resizeable]="false"
                  [draggable]="false"
                  [canAutoResize]="false"
                >
                  <ng-template let-row="row" ngx-datatable-cell-template>
                    <button
                      mat-icon-button
                      class="delete-button"
                      matTooltip="Click to delete"
                      (click)="beforeDelete('map', row)"
                    >
                      <mat-icon>delete_forever</mat-icon>
                    </button>
                  </ng-template>
                </ngx-datatable-column>
                <ngx-datatable-column
                  name="Name"
                  [width]="80"
                  [cellClass]="getNameCellClass"
                ></ngx-datatable-column>
                <ngx-datatable-column name="Value" [width]="500">
                  <ng-template
                    let-row="row"
                    let-value="value"
                    ngx-datatable-cell-template
                  >
                    <hi-data-table
                      [rows]="value"
                      [sorts]="sorts"
                      [columns]="columns.simpleConfigs"
                      [deletable]="editable"
                      [insertable]="editable"
                      (update)="edited('map', $event, row.name)"
                      (create)="created('map', $event, row.name)"
                      (delete)="edited('map', $event, row.name, true)"
                    >
                    </hi-data-table>
                  </ng-template>
                </ngx-datatable-column>
                <ngx-datatable-footer>
                  <ng-template
                    ngx-datatable-footer-template
                    let-rowCount="rowCount"
                    let-pageSize="pageSize"
                    let-curPage="curPage"
                  >
                    <section
                      class="footer"
                      fxLayout="row"
                      fxLayoutAlign="space-between center"
                    >
                      <button mat-button *ngIf="editable" (click)="onCreate('map')">
                        <mat-icon>add</mat-icon>
                        Add new entry
                      </button>
                      <section>{{ rowCount }} total</section>
                      <section>
                        <datatable-pager
                          [pagerLeftArrowIcon]="'datatable-icon-left'"
                          [pagerRightArrowIcon]="'datatable-icon-right'"
                          [pagerPreviousIcon]="'datatable-icon-prev'"
                          [pagerNextIcon]="'datatable-icon-skip'"
                          [page]="curPage"
                          [size]="pageSize"
                          [count]="rowCount"
                          [hidden]="!(rowCount / pageSize > 1)"
                          (change)="mapTable.onFooterPage($event)"
                        >
                        </datatable-pager>
                      </section>
                    </section>
                  </ng-template>
                </ngx-datatable-footer>
              </ngx-datatable>
            </mat-card-content>
          </mat-card>
        </section>
      </section>
    </section>

`./node-viewer.component.scss`

    @use '@angular/material' as mat;
    @import 'src/theme.scss';

    .node-viewer {
      padding: 10px;
    }

    .primary {
      color: mat.get-color-from-palette($hi-primary);
    }

    .search-form-field {
      width: 300px;
      padding: 10px 0 0 5px;
    }

    mat-card {
      margin-bottom: 10px;
    }

    ngx-datatable {
      word-break: break-all;
    }

    .footer {
      width: 100%;
      padding: 0 20px;
    }

    .delete-button {
      width: 24px;
      height: 24px;
      line-height: 24px;

      .mat-icon {
        @include md-icon-size(20px);

        &:hover {
          color: mat.get-color-from-palette($hi-warn);
        }
      }
    }

Legend
Html element
Component
Html element with directive

# results matching ""

# No results matching ""