import { Component, ChangeDetectorRef, Input, ViewChild, forwardRef, Output, EventEmitter, OnInit, OnDestroy, AfterViewInit, inject, } from "@angular/core"; import { NG_VALUE_ACCESSOR, Validators, ValidatorFn } from "@angular/forms"; import { Subject } from "rxjs"; import { takeUntil } from "rxjs/operators"; import { LabelWrapper } from "../../wrappers/label-wrapper"; import { SamFormService } from "../../form-service"; import { KeyHelper } from "../../utilities/key-helper/key-helper"; import { SamFormControl } from "../../form-controls/sam-form-control"; @Component({ selector: "sam-dollar", templateUrl: "dollar.template.html", providers: [ { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => SamDollarComponent), multi: true, }, ], standalone: false, }) export class SamDollarComponent extends SamFormControl implements OnInit, OnDestroy, AfterViewInit { samFormService: SamFormService; cdr: ChangeDetectorRef; /** * Optional text to be displayed when the text area is empty */ @Input() public placeholder: string; /** * (deprecated) Lose focus event emit */ @Output() public onBlur: EventEmitter = new EventEmitter(); /** * Lose focus event emit */ @Output() public blur: EventEmitter = new EventEmitter(); @ViewChild(LabelWrapper, { static: true }) public wrapper: LabelWrapper; // type=number starts adding letter 'e' if a max length is not enforced strictMaxLength = 16; attrType = "text"; previousValue = null; blurDisabled = false; private ngUnsubscribe: Subject = new Subject(); constructor() { const samFormService = inject(SamFormService); const cdr = inject(ChangeDetectorRef); super(); this.samFormService = samFormService; this.cdr = cdr; } public ngOnInit() { this.setupComponent(); } protected setupComponent() { if (!this.id) { throw new Error( " requires a [id] parameter\ for 508 compliance" ); } if (!this.control) { return; } const validators: ValidatorFn[] = []; if (this.control.validator) { validators.push(this.control.validator); } if (this.required) { validators.push(Validators.required); } this.control.setValidators(validators); if (!this.useFormService) { this.control.statusChanges .pipe(takeUntil(this.ngUnsubscribe)) .subscribe(() => { this.wrapper.formatErrors(this.control); this.cdr.detectChanges(); }); } else { this.samFormService.formEventsUpdated$.subscribe( (evt: { root?: unknown; eventType?: string }) => { if ( (!evt.root || evt.root === this.control.root) && evt.eventType && evt.eventType === "submit" ) { this.wrapper.formatErrors(this.control); } else if ( (!evt.root || evt.root === this.control.root) && evt.eventType && evt.eventType === "reset" ) { this.wrapper.clearError(); } } ); } } public ngOnDestroy() { this.cdr.detach(); this.ngUnsubscribe.next(null); this.ngUnsubscribe.complete(); } public ngAfterViewInit() { if (this.control) { this.wrapper.formatErrors(this.control); this.cdr.detectChanges(); } } public numberWithCommas(x) { const parts = x.toString().split("."); parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ","); return parts.join("."); } public onFocus() { this.onTouched(); if (!this.value || this.value === "$") { return; } this.value = this.dollarToStr(this.value); this.attrType = "number"; this.cdr.detectChanges(); } public onLoseFocus() { if (this.blurDisabled) { return; } this.attrType = "text"; if (this.value === "" || this.value == null) { this.emitChange(""); this.onBlur.emit(true); this.blur.emit(true); return; } const value = this.strToDollar(this.value); if (value !== this.previousValue) { this.emitChange(value); this.previousValue = value; } this.value = this.numberWithCommas(value); this.onBlur.emit(true); this.blur.emit(true); this.cdr.detectChanges(); } public dollarToStr(value) { let strValue = value ? value : ""; strValue = strValue.replace(/\$/g, ""); strValue = strValue.replace(/,/g, ""); return strValue; } public onKeyInput(event) { if ( ("" + this.value).length > this.strictMaxLength && KeyHelper.getNumberFromKey(event) !== undefined ) { return false; } } public roundForCurrency(number) { if (isNaN(number)) { return ""; } number = parseFloat("" + Math.round(number * 100) / 100).toFixed(2); number = "" + number; return number; } public strToDollar(value) { if (!value) { return ""; } let dollarVal = value.trim(); if (dollarVal !== "") { dollarVal = this.roundForCurrency(dollarVal); dollarVal = this.numberWithCommas(dollarVal); dollarVal = "$" + dollarVal; } return dollarVal; } public onInputChange() { const value: string = this.strToDollar(this.value); this.emitChange(value); } public emitChange(value) { this.onChange(value); if (!value) { this.control.setErrors({ required: true }); } else { this.control.setErrors(null); } this.control.updateValueAndValidity(); this.cdr.detectChanges(); } }