src/app/editor/components/edit-record/edit-record.component.ts

Implements

OnInit

Metadata

Relationships

Used by

Depends on

Index

Properties
Methods
Inputs
Outputs
HostListeners

Constructor

constructor(sasStoreService: SasStoreService, helperService: HelperService)
Parameters :
Name Type Optional
sasStoreService SasStoreService No
helperService HelperService No

Inputs

cellValidation
Type : DcValidation[]
Default value : []
currentRecord
Type : EditRecordModal
currentRecordErrors
Type : number[]
Default value : []
currentRecordIndex
Type : number
Default value : -1
currentRecordLoadings
Type : number[]
Default value : []
currentRecordValidator
Type : DcValidator | undefined
filter
Type : boolean
Default value : false
headerPks
Type : string[]
Default value : []
libds
Type : string | undefined
queryFilter
Type : any
recordAction
Type : string | null
Default value : null
submitLoading
Type : boolean
Default value : false

Outputs

onNextRecord
Type : EventEmitter<any>
onPreviousRecord
Type : EventEmitter<any>
onRecordChange
Type : EventEmitter<EditRecordModal>
onRecordDropdownChanged
Type : EventEmitter<EditRecordDropdownChangeEvent>
onRecordEditClose
Type : EventEmitter<any>
onRecordEditConfirm
Type : EventEmitter<any>
onRecordInputFocused
Type : EventEmitter<EditRecordInputFocusedEvent>

HostListeners

document:keydown.escape
document:keydown.escape()

Close the modal on Escape (cancel, like the Close button). A native picker swallows the first Escape to dismiss itself, so the modal only closes once focus is back on the form.

Methods

closeRecordEdit
closeRecordEdit()

Close edit record modal without applying the changes

Returns : void
confirmRecordEdit
confirmRecordEdit()

Close edit record modal and apply changes by emitting output event

Returns : void
Public copyToClip
copyToClip(text: string)
Parameters :
Name Type Optional
text string No
Returns : void
Async generateEditRecordUrl
generateEditRecordUrl()
Returns : any
isColPk
isColPk(col: string)
Parameters :
Name Type Optional
col string No
Returns : boolean
ngOnInit
ngOnInit()
Returns : void
onEscapeKey
onEscapeKey()
Decorators :
@HostListener('document:keydown.escape')

Close the modal on Escape (cancel, like the Close button). A native picker swallows the first Escape to dismiss itself, so the modal only closes once focus is back on the form.

Returns : void
onNextRecordClick
onNextRecordClick()
Returns : void
onPreviousRecordClick
onPreviousRecordClick()
Returns : void
onRecordDropdownChange
onRecordDropdownChange(colName: string, col: number)

Emitting output event when dropdown (autocomplete) input in any col change

Parameters :
Name Type Optional Description
colName string No

column name (key)

col number No

column index

Returns : void
onRecordInputFocus
onRecordInputFocus(event: any, colName: number)

Emitting output event when input is focused (clicked on) so we can run a dynamic cell validation Since that bit must be run from the parent component (editor.component) Result is then applied in the cellValidation variable and automatically updated in this component.

Parameters :
Name Type Optional Description
event any No

input event

colName number No

column name (key)

Returns : void
recordDateChange
recordDateChange(event: Event, colKey: string)

Fired when the native date picker (intl-date) changes. A native <input type="date"> value is always ISO YYYY-MM-DD, so it is stored as-is. Mirrors recordTimeChange / recordDatetimeChange.

Parameters :
Name Type Optional Description
event Event No

native change event

colKey string No

column name (key)

Returns : void
recordDatetimeChange
recordDatetimeChange(event: Event, colKey: string)

Fired when the native datetime picker (intl-datetime) changes. Converts the native YYYY-MM-DDTHH:mm[:ss] into SAS' stored YYYY-MM-DD HH:mm:ss, padding seconds. Mirrors the IntlDatetimeEditor conversion used by the grid.

Parameters :
Name Type Optional Description
event Event No

native change event

colKey string No

column name (key)

Returns : void
Async recordInputChange
recordInputChange(event: any, colName: string)
Parameters :
Name Type Optional
event any No
colName string No
Returns : Promise<void>
recordInputPaste
recordInputPaste(event: any)
Parameters :
Name Type Optional
event any No
Returns : void
recordTimeChange
recordTimeChange(event: Event, colKey: string)

Fired when the native time picker (intl-time) changes. Stores 24h HH:mm:ss, padding the seconds the browser may omit. Mirrors the IntlDatetimeEditor conversion used by the HOT grid.

Parameters :
Name Type Optional Description
event Event No

native change event

colKey string No

column name (key)

Returns : void
Private revalidateRecordCol
revalidateRecordCol(colName: string, value: any)

Re-runs validation for a column after a native picker change (the picker handlers set the value programmatically, which does not fire the textarea's input handler). Mirrors recordInputChange's validate + state update.

Parameters :
Name Type Optional
colName string No
value any No
Returns : void
toNativeDatetime
toNativeDatetime(value: any)

Stored YYYY-MM-DD HH:mm:ss → native <input type="datetime-local"> value YYYY-MM-DDTHH:mm:ss, so the picker opens at the current value.

Parameters :
Name Type Optional
value any No
Returns : string
trackByFn
trackByFn(index: number, item: any)
Parameters :
Name Type Optional
index number No
item any No
Returns : number
Private tryAutoPopulateNotNull
tryAutoPopulateNotNull(event: any, colName: string, colRules: DcValidation | undefined, value: any)

Auto-populates NOTNULL default value when the field is empty and has a default

Parameters :
Name Type Optional
event any No
colName string No
colRules DcValidation | undefined No
value any No
Returns : void
Private updateValidationState
updateValidationState(colName: string, valid: boolean)

Updates the invalid columns list based on validation result

Parameters :
Name Type Optional
colName string No
valid boolean No
Returns : void
Private updateWarningState
updateWarningState(colName: string, value: any)

Updates the SOFTREGEX warning columns list — the modal's equivalent of makeRegexWarningRenderer in the grid (see DcValidator.failsSoftRegex).

Parameters :
Name Type Optional
colName string No
value any No
Returns : void
Async validateRecordCol
validateRecordCol(cellValidation: any, cellValue: any)

Runs native HOT validator against cell value

Parameters :
Name Type Optional Description
cellValidation any No

column rules

cellValue any No

value in the cell that is beign validated

Returns : Promise<boolean>

Promise boolean - wether valid or invalid

Properties

Public addRecordLoading
Type : boolean
Default value : false
Public addRecordUrl
Type : string | null
Default value : null
Public currentRecordInvalidCols
Type : string[]
Default value : []
Public currentRecordWarningCols
Type : string[]
Default value : []
Public generatedRecordUrl
Type : string | null
Default value : null
Public generateEditRecordUrlLoading
Type : boolean
Default value : false
originalOrder
Type : unknown
Default value : () => {...}
Public recordNewOrPkModified
Type : boolean
Default value : false
Public validatorTimeout
Type : any
import { KeyValue } from '@angular/common'
import {
  Component,
  EventEmitter,
  HostListener,
  Input,
  OnInit,
  Output,
  ViewEncapsulation
} from '@angular/core'
import { ValidateFilterSASResponse } from 'src/app/models/sas/validate-filter.model'
import { QueryClause } from 'src/app/models/TableData'
import { HelperService } from 'src/app/services/helper.service'
import { SasStoreService } from 'src/app/services/sas-store.service'
import { DcValidator } from 'src/app/shared/dc-validator/dc-validator'
import { DcValidation } from 'src/app/shared/dc-validator/models/dc-validation.model'
import { isEmpty } from 'src/app/shared/dc-validator/utils/isEmpty'
import {
  EditRecordDropdownChangeEvent,
  EditRecordInputFocusedEvent
} from '../../models/edit-record/edit-record-events'
import { EditRecordModal } from '../../models/EditRecordModal'

@Component({
  selector: 'app-edit-record',
  templateUrl: './edit-record.component.html',
  styleUrls: ['./edit-record.component.scss'],
  encapsulation: ViewEncapsulation.None,
  standalone: false
})
export class EditRecordComponent implements OnInit {
  @Input() currentRecord!: EditRecordModal
  @Input() recordAction: string | null = null
  @Input() libds: string | undefined
  @Input() queryFilter: any
  @Input() filter: boolean = false
  @Input() submitLoading: boolean = false
  @Input() headerPks: string[] = []
  @Input() cellValidation: DcValidation[] = []
  @Input() currentRecordIndex: number = -1
  @Input() currentRecordLoadings: number[] = []
  @Input() currentRecordErrors: number[] = []
  @Input() currentRecordValidator: DcValidator | undefined

  @Output() onRecordChange: EventEmitter<EditRecordModal> =
    new EventEmitter<EditRecordModal>()
  @Output() onRecordInputFocused: EventEmitter<EditRecordInputFocusedEvent> =
    new EventEmitter<EditRecordInputFocusedEvent>()
  @Output()
  onRecordDropdownChanged: EventEmitter<EditRecordDropdownChangeEvent> =
    new EventEmitter<EditRecordDropdownChangeEvent>()
  @Output() onRecordEditClose: EventEmitter<any> = new EventEmitter<any>()
  @Output() onRecordEditConfirm: EventEmitter<any> = new EventEmitter<any>()
  @Output() onNextRecord: EventEmitter<any> = new EventEmitter<any>()
  @Output() onPreviousRecord: EventEmitter<any> = new EventEmitter<any>()

  public currentRecordInvalidCols: string[] = []
  public currentRecordWarningCols: string[] = []
  public generateEditRecordUrlLoading: boolean = false
  public generatedRecordUrl: string | null = null
  public addRecordUrl: string | null = null
  public recordNewOrPkModified: boolean = false
  public addRecordLoading: boolean = false
  public validatorTimeout: any

  constructor(
    private sasStoreService: SasStoreService,
    private helperService: HelperService
  ) {}

  ngOnInit(): void {}

  /**
   * Runs native HOT validator against cell value
   * @param cellValidation column rules
   * @param cellValue value in the cell that is beign validated
   * @returns Promise boolean - wether valid or invalid
   */
  async validateRecordCol(
    cellValidation: any,
    cellValue: any
  ): Promise<boolean> {
    return new Promise((resolve, reject) => {
      this.currentRecordValidator?.executeHotValidator(
        cellValidation,
        cellValue,
        (valid: boolean) => {
          resolve(valid)
        }
      )
    })
  }

  /**
   * Fired when the native date picker (intl-date) changes. A native
   * `<input type="date">` value is always ISO `YYYY-MM-DD`, so it is stored
   * as-is. Mirrors recordTimeChange / recordDatetimeChange.
   * @param event native <input type="date"> change event
   * @param colKey column name (key)
   */
  recordDateChange(event: Event, colKey: string) {
    const value = (event.target as HTMLInputElement).value // YYYY-MM-DD
    if (!value || !this.currentRecord) return
    this.currentRecord[colKey] = value
    this.revalidateRecordCol(colKey, value)
  }

  /**
   * Stored `YYYY-MM-DD HH:mm:ss` → native `<input type="datetime-local">` value
   * `YYYY-MM-DDTHH:mm:ss`, so the picker opens at the current value.
   */
  toNativeDatetime(value: any): string {
    if (typeof value !== 'string') return ''
    return value.includes(' ') ? value.replace(' ', 'T') : value
  }

  /**
   * Re-runs validation for a column after a native picker change (the picker
   * handlers set the value programmatically, which does not fire the textarea's
   * input handler). Mirrors recordInputChange's validate + state update.
   */
  private revalidateRecordCol(colName: string, value: any) {
    const colRules = this.currentRecordValidator?.getRule(colName)

    this.validateRecordCol(colRules, value).then((valid: boolean) =>
      this.updateValidationState(colName, valid)
    )

    this.updateWarningState(colName, value)
  }

  /**
   * Fired when the native time picker (intl-time) changes. Stores 24h
   * `HH:mm:ss`, padding the seconds the browser may omit. Mirrors the
   * IntlDatetimeEditor conversion used by the HOT grid.
   * @param event native <input type="time"> change event
   * @param colKey column name (key)
   */
  recordTimeChange(event: Event, colKey: string) {
    const value = (event.target as HTMLInputElement).value // HH:mm[:ss]
    if (!value || !this.currentRecord) return
    const stored = value.length === 5 ? `${value}:00` : value
    this.currentRecord[colKey] = stored
    this.revalidateRecordCol(colKey, stored)
  }

  /**
   * Fired when the native datetime picker (intl-datetime) changes. Converts the
   * native `YYYY-MM-DDTHH:mm[:ss]` into SAS' stored `YYYY-MM-DD HH:mm:ss`,
   * padding seconds. Mirrors the IntlDatetimeEditor conversion used by the grid.
   * @param event native <input type="datetime-local"> change event
   * @param colKey column name (key)
   */
  recordDatetimeChange(event: Event, colKey: string) {
    const value = (event.target as HTMLInputElement).value // YYYY-MM-DDTHH:mm[:ss]
    if (!value || !this.currentRecord) return
    const [date, time] = value.split('T')
    const stored = `${date} ${time.length === 5 ? `${time}:00` : time}`
    this.currentRecord[colKey] = stored
    this.revalidateRecordCol(colKey, stored)
  }

  /**
   * Close edit record modal and apply changes by emitting output event
   */
  confirmRecordEdit() {
    if (this.currentRecordInvalidCols.length < 1) {
      this.onRecordChange.emit(this.currentRecord)
    }
  }

  /**
   * Close edit record modal without applying the changes
   */
  closeRecordEdit() {
    this.onRecordEditClose.emit()
  }

  /**
   * Close the modal on Escape (cancel, like the Close button). A native picker
   * swallows the first Escape to dismiss itself, so the modal only closes once
   * focus is back on the form.
   */
  @HostListener('document:keydown.escape')
  onEscapeKey() {
    this.closeRecordEdit()
  }

  /**
   * Emitting output event when dropdown (autocomplete) input in any col change
   * @param colName column name (key)
   * @param col column index
   */
  onRecordDropdownChange(colName: string, col: number) {
    this.onRecordDropdownChanged.emit({ colName, col })
  }

  /**
   * Emitting output event when input is focused (clicked on) so we can run a `dynamic cell validation`
   * Since that bit must be run from the parent component (editor.component)
   * Result is then applied in the `cellValidation` variable and automatically updated in this component.
   * @param event input event
   * @param colName column name (key)
   */
  onRecordInputFocus(event: any, colName: number) {
    this.onRecordInputFocused.emit({ event, colName })
  }

  recordInputPaste(event: any) {
    setTimeout(() => {
      //Trim space at the end
      event.target.value = event.target.value.replace(/\s+$/, '')
    }, 0)
  }

  async recordInputChange(event: any, colName: string): Promise<void> {
    const colRules = this.currentRecordValidator?.getRule(colName)
    const value = event.target.value

    this.helperService.debounceCall(300, () => {
      this.validateRecordCol(colRules, value).then((valid: boolean) => {
        this.updateValidationState(colName, valid)

        if (!valid) {
          this.tryAutoPopulateNotNull(event, colName, colRules, value)
        }
      })

      this.updateWarningState(colName, value)
    })
  }

  /**
   * Updates the invalid columns list based on validation result
   */
  private updateValidationState(colName: string, valid: boolean): void {
    const index = this.currentRecordInvalidCols.indexOf(colName)

    if (valid && index > -1) {
      this.currentRecordInvalidCols.splice(index, 1)
    } else if (!valid && index < 0) {
      this.currentRecordInvalidCols.push(colName)
    }
  }

  /**
   * Updates the SOFTREGEX warning columns list — the modal's equivalent of
   * makeRegexWarningRenderer in the grid (see DcValidator.failsSoftRegex).
   */
  private updateWarningState(colName: string, value: any): void {
    const failsSoftRegex =
      this.currentRecordValidator?.failsSoftRegex(colName, value) ?? false
    const index = this.currentRecordWarningCols.indexOf(colName)

    if (!failsSoftRegex && index > -1) {
      this.currentRecordWarningCols.splice(index, 1)
    } else if (failsSoftRegex && index < 0) {
      this.currentRecordWarningCols.push(colName)
    }
  }

  /**
   * Auto-populates NOTNULL default value when the field is empty and has a default
   */
  private tryAutoPopulateNotNull(
    event: any,
    colName: string,
    colRules: DcValidation | undefined,
    value: any
  ): void {
    if (
      !isEmpty(value) ||
      !this.currentRecordValidator ||
      !this.currentRecord
    ) {
      return
    }

    const defaultValue =
      this.currentRecordValidator.getNotNullDefaultValue(colName)
    if (defaultValue === undefined) return

    this.currentRecord[colName] = defaultValue
    event.target.value = defaultValue

    this.validateRecordCol(colRules, defaultValue).then((isValid: boolean) => {
      this.updateValidationState(colName, isValid)
    })

    this.updateWarningState(colName, defaultValue)
  }

  onNextRecordClick() {
    this.onNextRecord.emit()
  }

  onPreviousRecordClick() {
    this.onPreviousRecord.emit()
  }

  public copyToClip(text: string) {
    navigator.clipboard.writeText(text)
    this.generatedRecordUrl = text
  }

  async generateEditRecordUrl() {
    if (this.generatedRecordUrl) {
      this.copyToClip(this.generatedRecordUrl)
    } else {
      this.generateEditRecordUrlLoading = true

      const filterQueryClauseTable: QueryClause[] = []

      this.headerPks.forEach((key: string) => {
        let type = 'C'
        let rawValue = ''
        for (let i = 0; i < this.cellValidation.length; i++) {
          const obj = this.cellValidation[i]
          if (obj.data === key) {
            if (
              obj.type === 'numeric' ||
              obj.type === 'intl-date' ||
              obj.type === 'intl-time' ||
              obj.type === 'intl-datetime'
            ) {
              type = 'N'
            }
            break
          }
        }

        if (type === 'C') {
          rawValue = `'${this.currentRecord[key]}'`
        } else {
          rawValue = this.currentRecord[key].toString()
        }

        filterQueryClauseTable.push({
          GROUP_LOGIC: 'AND',
          SUBGROUP_LOGIC: 'AND',
          SUBGROUP_ID: 0,
          VARIABLE_NM: key,
          OPERATOR_NM: '=',
          RAW_VALUE: rawValue
        })
      })

      if (filterQueryClauseTable.length > 0 && this.libds) {
        await this.sasStoreService
          .saveQuery(this.libds, filterQueryClauseTable)
          .then((res: ValidateFilterSASResponse) => {
            const id = res.result[0].FILTER_RK
            const table = res.result[0].FILTER_TABLE
            this.queryFilter = { id: id, table: table }

            //copy to clipboard
            const editLink =
              location.href.split('#')[0] +
              '#/editor/edit-record/' +
              this.queryFilter.table +
              '/' +
              this.queryFilter.id

            this.copyToClip(editLink)

            this.generateEditRecordUrlLoading = false
            this.filter = false
          })
          .catch((err: any) => {
            this.submitLoading = false
          })
      }
    }
  }

  isColPk(col: string) {
    return this.headerPks.indexOf(col) > -1
  }

  originalOrder = (
    a: KeyValue<string, string>,
    b: KeyValue<string, string>
  ): number => {
    return 0
  }

  trackByFn(index: number, item: any): number {
    return index
  }
}
<clr-modal
  [clrModalOpen]="true"
  [clrModalClosable]="false"
  id="recordModalRef"
  class="record-edit-modal position-relative"
>
  <h3 class="modal-title">
    {{ recordAction === 'ADD' ? 'Add' : 'Edit' }} record in
    {{ (libds?.split('.'))![0] }}.{{ (libds?.split('.'))![1] }}

    <ng-container *ngIf="recordAction === 'EDIT'">
      <span
        *ngIf="!currentRecord?.noLinkOption"
        class="position-absolute generate-record-url"
        >{{ generatedRecordUrl }}</span
      >
      <button
        *ngIf="!currentRecord?.noLinkOption"
        (click)="generateEditRecordUrl()"
        [disabled]="generateEditRecordUrlLoading"
        class="btn btn-link generate-record-url-button position-absolute"
      >
        {{
          generatedRecordUrl
            ? 'copy to clipboard'
            : generateEditRecordUrlLoading
              ? 'Generating url...'
              : 'Link to this record'
        }}
      </button>
    </ng-container>
  </h3>

  <div class="modal-body">
    <div
      *ngIf="addRecordLoading"
      class="d-flex justify-content-center align-items-center w-100 py-10"
    >
      <span class="spinner"> Loading... </span>
    </div>

    <ng-container *ngIf="!addRecordLoading">
      <ng-container
        *ngFor="
          let col of currentRecord | keyvalue: originalOrder;
          let colIndex = index;
          trackBy: trackByFn
        "
      >
        <ng-container
          *ngIf="
            col.key !== 'Action' &&
            col.key !== '_____DELETE__THIS__RECORD_____' &&
            col.key !== 'noLinkOption'
          "
        >
          <div class="column-entry clr-flex-column clr-flex-md-row">
            <div class="name-input-row">
              <p class="font-weight-bold">
                {{ col.key }}
                <clr-icon *ngIf="isColPk(col.key)" shape="key"></clr-icon>
              </p>

              <p class="cell-desc">
                {{ currentRecordValidator?.getRule(col.key)?.desc }}
              </p>
            </div>

            <div
              [ngSwitch]="currentRecordValidator?.getRule(col.key)?.type"
              class="inputs-wrapper position-relative"
            >
              <span
                *ngIf="currentRecordLoadings.includes(colIndex)"
                class="spinner spinner-inline flex-unset position-absolute entry-input-left-offset"
              >
                Loading...
              </span>

              <clr-icon
                *ngIf="currentRecordErrors.includes(colIndex)"
                class="flex-unset position-absolute entry-input-left-offset"
                shape="error-standard"
                status="warning"
              ></clr-icon>

              <!-- SOFTREGEX: value doesn't match the expected pattern, but
                   submission is still allowed (unlike HARDREGEX, which is
                   surfaced via the existing invalid-data class below). -->
              <clr-icon
                *ngIf="
                  !currentRecordErrors.includes(colIndex) &&
                  currentRecordWarningCols.includes(col.key)
                "
                class="flex-unset position-absolute entry-input-left-offset"
                shape="error-standard"
                status="warning"
                title="Value does not match the expected pattern"
              ></clr-icon>

              <ng-container *ngSwitchCase="'numeric'">
                <clr-input-container
                  *ngIf="
                    ['numeric', undefined].includes(
                      $any(currentRecordValidator?.getRule(col.key)?.editor)
                    )
                  "
                  class="numeric-input"
                  [class.invalid-data]="
                    currentRecordInvalidCols.includes(col.key)
                  "
                >
                  <input
                    clrInput
                    type="text"
                    class="not-char"
                    (focusin)="onRecordInputFocus($event, colIndex)"
                    (paste)="recordInputPaste($event)"
                    (input)="
                      recordInputChange($event, col.key);
                      currentRecord!.noLinkOption = true
                    "
                    [(ngModel)]="currentRecord![col.key]"
                  />
                </clr-input-container>

                <div
                  *ngIf="
                    ['autocomplete', 'autocomplete.custom'].includes(
                      $any(currentRecordValidator?.getRule(col.key)?.editor)
                    )
                  "
                >
                  <ng-container
                    *ngTemplateOutlet="
                      autocomplete;
                      context: { col: col, colIndex: colIndex }
                    "
                  ></ng-container>
                </div>
              </ng-container>

              <ng-container *ngSwitchCase="undefined">
                <clr-textarea-container
                  *ngIf="
                    [undefined].includes(
                      $any(currentRecordValidator?.getRule(col.key)?.editor)
                    )
                  "
                >
                  <textarea
                    #charTextarea
                    clrTextarea
                    (focusin)="onRecordInputFocus($event, colIndex)"
                    (paste)="recordInputPaste($event)"
                    (input)="recordInputChange($event, col.key)"
                    [class.invalid-data]="
                      currentRecordInvalidCols.includes(col.key)
                    "
                    [rows]="col.value.length > 80 ? 6 : 1"
                    [(ngModel)]="currentRecord![col.key]"
                    [maxLength]="
                      currentRecordValidator?.getRule(col.key)?.length
                    "
                    [class.not-char]="
                      currentRecordValidator?.getRule(col.key)?.type
                    "
                  >
                  </textarea>
                  <clr-control-helper
                    >{{ charTextarea.value.length }} /
                    {{
                      currentRecordValidator?.getRule(col.key)?.length
                    }}</clr-control-helper
                  >
                </clr-textarea-container>

                <div
                  *ngIf="
                    ['autocomplete', 'autocomplete.custom'].includes(
                      $any(currentRecordValidator?.getRule(col.key)?.editor)
                    )
                  "
                >
                  <ng-container
                    *ngTemplateOutlet="
                      autocomplete;
                      context: { col: col, colIndex: colIndex }
                    "
                  ></ng-container>
                </div>
              </ng-container>

              <div class="date-field" *ngSwitchCase="'intl-time'">
                <input
                  type="time"
                  step="1"
                  class="date-input"
                  [attr.aria-label]="col.key"
                  [class.invalid-data]="
                    currentRecordInvalidCols.includes(col.key)
                  "
                  [value]="currentRecord[col.key]"
                  (change)="recordTimeChange($event, col.key)"
                />
              </div>

              <div class="date-field" *ngSwitchCase="'intl-datetime'">
                <input
                  type="datetime-local"
                  step="1"
                  class="date-input"
                  [attr.aria-label]="col.key"
                  [class.invalid-data]="
                    currentRecordInvalidCols.includes(col.key)
                  "
                  [value]="toNativeDatetime(currentRecord[col.key])"
                  (change)="recordDatetimeChange($event, col.key)"
                />
              </div>

              <div class="date-field" *ngSwitchCase="'intl-date'">
                <input
                  type="date"
                  class="date-input"
                  [attr.aria-label]="col.key"
                  [class.invalid-data]="
                    currentRecordInvalidCols.includes(col.key)
                  "
                  [value]="currentRecord[col.key]"
                  (change)="recordDateChange($event, col.key)"
                />
              </div>
              <div *ngSwitchCase="'autocomplete'">
                <ng-container
                  *ngTemplateOutlet="
                    autocomplete;
                    context: { col: col, colIndex: colIndex }
                  "
                ></ng-container>
              </div>
            </div>
          </div>

          <hr class="mt-10" />
        </ng-container>
      </ng-container>
    </ng-container>
  </div>
  <div
    class="modal-footer p-0 clr-gap-5 clr-gap-md-0 clr-flex-column clr-flex-md-row"
  >
    <clr-alert
      *ngIf="currentRecordInvalidCols.length > 0"
      class="validation-info-alert"
      [style.visibility]="
        currentRecordInvalidCols.length > 0 ? 'visible' : 'hidden'
      "
      [clrAlertClosable]="false"
      [clrAlertType]="'danger'"
    >
      <clr-alert-item>
        <span class="alert-text"> Data Validation Errors - Please Review </span>
      </clr-alert-item>
    </clr-alert>

    <div *ngIf="currentRecordInvalidCols.length < 1" class="prev-next">
      <button
        type="button"
        class="btn btn-outline focusable"
        tabindex="0"
        aria-label="Previous record"
        (click)="onPreviousRecordClick()"
        [disabled]="currentRecordInvalidCols.length > 0"
      >
        <clr-icon shape="angle left" size="23"></clr-icon>
      </button>
      <p>
        Current row: <strong> {{ currentRecordIndex + 1 }} </strong>
      </p>
      <button
        type="button"
        class="btn btn-outline focusable"
        tabindex="0"
        aria-label="Next record"
        (click)="onNextRecordClick()"
        [disabled]="currentRecordInvalidCols.length > 0"
      >
        <clr-icon shape="angle right" size="23"></clr-icon>
      </button>
    </div>

    <div>
      <button
        type="button"
        class="btn btn-outline focusable mr-5i"
        (click)="currentRecord!.noLinkOption = false; closeRecordEdit()"
      >
        Cancel
      </button>
      <button
        type="button"
        class="btn btn-primary focusable"
        (click)="confirmRecordEdit()"
        [disabled]="currentRecordInvalidCols.length > 0"
      >
        Confirm
      </button>
    </div>
  </div>

  <ng-template #autocomplete let-col="col" let-colIndex="colIndex">
    <div
      *ngIf="
        currentRecordValidator?.hasDqRules(col.key, [
          'SOFTSELECT',
          'SOFTSELECT_HOOK'
        ]);
        else other_than_soft_select
      "
    >
      <app-soft-select
        (focusin)="onRecordInputFocus($event, colIndex)"
        [(value)]="currentRecord[col.key]"
        [inputId]="col.key"
        (onInputEvent)="onRecordDropdownChange(col.key, colIndex)"
      >
        <option
          [ngValue]="item"
          *ngFor="
            let item of $any(currentRecordValidator?.getRule(col.key)?.source)
          "
        >
          {{ item }}
        </option>
      </app-soft-select>
    </div>
    <ng-template #other_than_soft_select>
      <select
        (focusin)="onRecordInputFocus($event, colIndex)"
        [(ngModel)]="currentRecord![col.key]"
        clrSelect
      >
        <option
          *ngFor="
            let item of $any(currentRecordValidator?.getRule(col.key)?.source)
          "
          [value]="item"
        >
          {{ item }}
        </option>
      </select>
    </ng-template>
  </ng-template>
</clr-modal>

./edit-record.component.scss

Legend
Html element
Component
Html element with directive

results matching ""

    No results matching ""