src/app/editor/components/edit-record/edit-record.component.ts
| encapsulation | ViewEncapsulation.None |
| selector | app-edit-record |
| styleUrls | ./edit-record.component.scss |
| templateUrl | ./edit-record.component.html |
Properties |
|
Inputs |
Outputs |
HostListeners |
constructor(sasStoreService: SasStoreService, helperService: HelperService)
|
|||||||||
|
Parameters :
|
| 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
|
|
| 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>
|
|
| 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 :
Returns :
void
|
| Async generateEditRecordUrl |
generateEditRecordUrl()
|
|
Returns :
any
|
| isColPk | ||||||
isColPk(col: string)
|
||||||
|
Parameters :
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 :
Returns :
void
|
| onRecordInputFocus | ||||||||||||
onRecordInputFocus(event: any, colName: number)
|
||||||||||||
|
Emitting output event when input is focused (clicked on) so we can run a
Parameters :
Returns :
void
|
| recordDateChange | ||||||||||||
recordDateChange(event: Event, colKey: string)
|
||||||||||||
|
Fired when the native date picker (intl-date) changes. A native
Parameters :
Returns :
void
|
| recordDatetimeChange | ||||||||||||
recordDatetimeChange(event: Event, colKey: string)
|
||||||||||||
|
Fired when the native datetime picker (intl-datetime) changes. Converts the
native
Parameters :
Returns :
void
|
| Async recordInputChange |
recordInputChange(event: any, colName: string)
|
|
Returns :
Promise<void>
|
| recordInputPaste | ||||||
recordInputPaste(event: any)
|
||||||
|
Parameters :
Returns :
void
|
| recordTimeChange | ||||||||||||
recordTimeChange(event: Event, colKey: string)
|
||||||||||||
|
Fired when the native time picker (intl-time) changes. Stores 24h
Parameters :
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.
Returns :
void
|
| toNativeDatetime | ||||||
toNativeDatetime(value: any)
|
||||||
|
Stored
Parameters :
Returns :
string
|
| trackByFn |
trackByFn(index: number, item: any)
|
|
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 :
Returns :
void
|
| Private updateValidationState |
updateValidationState(colName: string, valid: boolean)
|
|
Updates the invalid columns list based on validation result
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).
Returns :
void
|
| Async validateRecordCol | ||||||||||||
validateRecordCol(cellValidation: any, cellValue: any)
|
||||||||||||
|
Runs native HOT validator against cell value
Parameters :
Returns :
Promise<boolean>
Promise boolean - wether valid or invalid |
| 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