Using the calcite-input-number I would like to achieve leading zeros.
In the specific case the input ranges from 0 to 360 degrees.
So the following input should automatically show the exprected result within the input
0 | 000 |
1 | 001 |
10 | 010 |
100 | 100 |
360 | 360 |
My input currently looks like this:
<calcite-input-number
[min]="courseRange.min" [max]="courseRange.max" step="1"
label="Course (in Degrees)"
[value]="course()"
(calciteInputNumberChange)="refreshNumberChanged($event, course)"/>
refreshNumberChanged is used to write back the value into a signal. This replaces former ngModel syntax.
Solved! Go to Solution.
Now I created a workaround solution for me by using a simple calcite-input and doing some key-event-handling.
I already have achieved most of what I want except the small arrow-icons on the right of a typical calcite-input-number to increase/decrease the value. I didnt want to use dom-related code, I am still unsure if there is any other way being more preferrable. So if anyone has an idea on that too or can help with some code, she is very welcome.
So heres my code so far, consisting of:
- a component (ts part)with glue-code
- a component (html part) with just an inner calcite-input
- an other component integrating/calling my dedicated component
TS Part
import { Component, EventEmitter, input, Output } from "@angular/core";
import { CalciteComponentsModule } from "@esri/calcite-components-angular";
import { CalciteInputValidityStatus } from "../../calcite-input-validity-status.enum";
@Component({
selector: 'my-leading-zero-input',
templateUrl: './leading-zero-input.component.html',
imports: [CalciteComponentsModule],
})
export class LeadingZeroInputComponent {
/**
* Event emitter to write back changes of the input-components value.
*/
@Output() calciteInputNumberChange = new EventEmitter<any>();
/**
* If true, any inputs outside min and max will automatically overwritten by the exceeded limit.
*/
fillLimitsOnExceed = input<boolean | undefined>();
/**
* Lower limit of the course.
* Only applies if {@link fillLimitsOnExceed} is true.
*/
courseMin = input<number | undefined>();
/**
* Lower limit of the course.
* May applies if {@link fillLimitsOnExceed} is true.
* Also applies for automatically filling/trimming up/down to leading zeros based on the max digit count.
*/
courseMax = input<number | undefined>();
/**
* Programatically setting the value, e.g. initially.
*/
courseInput = input<number | undefined>();
/**
* Validation status for the course field. If invalid shows the error message defined with {@link validityCourseMessage}
*/
validityCourse = input<CalciteInputValidityStatus>(CalciteInputValidityStatus.idle);
/**
* Validation message shown if {@link validityCourse} has status invalid.
*/
validityCourseMessage = input<string>();
/**
* Event happening before any input is printed into the component.
* In contrast to onKeyDown this works on actual resulting content
* which includes dead-key-combinations like [^]+[f]
* Only Digit input is allowed.
* Does not affect other non-input interactions like copy/paste.
* @Param event containing the actual input
*/
onBeforeInput(event: InputEvent) {
const bla = this.validityCourse();
console.log(bla);
if (event.inputType === 'insertText' && !/^\d+$/.test(event.data ?? '')) {
event.preventDefault();
}
}
/**
* Event happening on pasting from clipboard.
* Prevents whole pasting if it contains any non digit char.
* @Param event containing the pasting input
*/
onPaste(event: ClipboardEvent) {
const text = event.clipboardData?.getData('text') ?? '';
if (!/^\d+$/.test(text)) {
event.preventDefault();
}
}
/**
* Event happening right after any input is printed into the component.
* In contrast to onKeyUp this works on actual resulting content
* which includes dead-key-combinations like [^]+[f]
* Does not affect other non-input interactions like copy/paste.
*
* Removes any non-digit from the input by replacing with an empty string.
* Basically this works like a fallback especially for usage of other browsers
* in case the onBeforeInput did not work.
*
* if {@link fillLimitsOnExceed} is true:
* - Checks if the current value exceeds min or max limits if defined and
* automatically sets the value to the specific limit if exceeded.
* - Excepts for an input of only zero(s):
* In case there is a minimum higher than zero,
* zero stil is allowed as input to support leading zeros.
*
* @Param event containing the actual value
*/
onInput(event: Event) {
const input = event.target as HTMLInputElement;
input.value = input.value.replace(/\D+/g, '');
// if filling and trimming is not desired, return
if (!this.fillLimitsOnExceed()) {
return;
}
const asNumber = Number.parseInt(input.value);
// set as min if value is lower
// nevertheless allow leading zero in case min is greater than 0 (for some reason)
const courseMin = this.courseMin();
if (courseMin && asNumber < courseMin && asNumber !== 0) {
input.value = courseMin.toString();
}
// set as max if value is higher
const courseMax = this.courseMax();
if (courseMax && asNumber > courseMax) {
input.value = courseMax.toString();
}
}
/**
* Event happening when any key is pressed down.
* Checks for Esc or Enter has been hit.
* If so causes a blur.
* @Param event containing the pressed key
*/
onKeyDown(event: KeyboardEvent) {
if (event.key === 'Escape' || event.key === 'Enter') {
(event.target as HTMLElement).blur();
}
}
/**
* Event happening when focus gets lost.
*
* In case a max limit is defined leading zeros may be add or trimmed.
* The desired digit-count is based on the max value if defined.
* If so checks if leading zeros are missing and automatically adds them.
* If there are already leading zeros and they are too many, they will be cut.
* @Param event containing the pressed key
*/
onBlur(event: FocusEvent) {
const courseMax = this.courseMax();
if (courseMax) {
// add leading zeros, fill til length of max
const input = event.target as HTMLInputElement;
input.value = input.value.replace(/^0+/, '');
input.value = input.value.padStart(courseMax.toString().length, '0');
}
}
}
HTML Part
<calcite-input
label="Kurs (in Grad)"
i18n-label="...course"
title="Kurs (in Grad)" i18n-title="...course"
[value]="courseInput()" (calciteInputChange)="calciteInputNumberChange.emit($event)"
(beforeinput)="onBeforeInput($event)"
(paste)="onPaste($event)"
(input)="onInput($event)"
(keydown)="onKeyDown($event)"
(blur)="onBlur($event)"
[status] = "validityCourse()" [validationMessage]="validityCourseMessage()"
validationIcon = "exclamation-mark-triangle" />
Integrating/Calling
<my-leading-zero-input
[courseInput]="course()" [courseMin]="courseRange.min" [courseMax]="courseRange.max"
[validityCourse] = "validityCourse()" [validityCourseMessage]="validityCourseMessage()"
(calciteInputNumberChange)="refreshNumberChanged($event, course)"/>
Hi there,
It is not possible to set leading 0s with either calcite-input-number or calcite-input of type="number". We have an enhancement request for this. Will update you once it is supported.
Would you mind giving your use case for needing 3 leading 0s? Thanks
sure.
According to the experts in nautical navigation degrees are always given as three digit number with leading rows.
Ah gotcha! Thanks for responding.
Is there any bug number or sth else to track on?
I would take this link into our documentation for the future.
Thanks.
Can you please submit an issue via Support Servces? We then will attach our internal issue to the support issue. This way, you will have a link to connect to. Thanks
can you describe me how to do so and share a link?
Hope this will not take too much time and data to submit.
Now I created a workaround solution for me by using a simple calcite-input and doing some key-event-handling.
I already have achieved most of what I want except the small arrow-icons on the right of a typical calcite-input-number to increase/decrease the value. I didnt want to use dom-related code, I am still unsure if there is any other way being more preferrable. So if anyone has an idea on that too or can help with some code, she is very welcome.
So heres my code so far, consisting of:
- a component (ts part)with glue-code
- a component (html part) with just an inner calcite-input
- an other component integrating/calling my dedicated component
TS Part
import { Component, EventEmitter, input, Output } from "@angular/core";
import { CalciteComponentsModule } from "@esri/calcite-components-angular";
import { CalciteInputValidityStatus } from "../../calcite-input-validity-status.enum";
@Component({
selector: 'my-leading-zero-input',
templateUrl: './leading-zero-input.component.html',
imports: [CalciteComponentsModule],
})
export class LeadingZeroInputComponent {
/**
* Event emitter to write back changes of the input-components value.
*/
@Output() calciteInputNumberChange = new EventEmitter<any>();
/**
* If true, any inputs outside min and max will automatically overwritten by the exceeded limit.
*/
fillLimitsOnExceed = input<boolean | undefined>();
/**
* Lower limit of the course.
* Only applies if {@link fillLimitsOnExceed} is true.
*/
courseMin = input<number | undefined>();
/**
* Lower limit of the course.
* May applies if {@link fillLimitsOnExceed} is true.
* Also applies for automatically filling/trimming up/down to leading zeros based on the max digit count.
*/
courseMax = input<number | undefined>();
/**
* Programatically setting the value, e.g. initially.
*/
courseInput = input<number | undefined>();
/**
* Validation status for the course field. If invalid shows the error message defined with {@link validityCourseMessage}
*/
validityCourse = input<CalciteInputValidityStatus>(CalciteInputValidityStatus.idle);
/**
* Validation message shown if {@link validityCourse} has status invalid.
*/
validityCourseMessage = input<string>();
/**
* Event happening before any input is printed into the component.
* In contrast to onKeyDown this works on actual resulting content
* which includes dead-key-combinations like [^]+[f]
* Only Digit input is allowed.
* Does not affect other non-input interactions like copy/paste.
* @Param event containing the actual input
*/
onBeforeInput(event: InputEvent) {
const bla = this.validityCourse();
console.log(bla);
if (event.inputType === 'insertText' && !/^\d+$/.test(event.data ?? '')) {
event.preventDefault();
}
}
/**
* Event happening on pasting from clipboard.
* Prevents whole pasting if it contains any non digit char.
* @Param event containing the pasting input
*/
onPaste(event: ClipboardEvent) {
const text = event.clipboardData?.getData('text') ?? '';
if (!/^\d+$/.test(text)) {
event.preventDefault();
}
}
/**
* Event happening right after any input is printed into the component.
* In contrast to onKeyUp this works on actual resulting content
* which includes dead-key-combinations like [^]+[f]
* Does not affect other non-input interactions like copy/paste.
*
* Removes any non-digit from the input by replacing with an empty string.
* Basically this works like a fallback especially for usage of other browsers
* in case the onBeforeInput did not work.
*
* if {@link fillLimitsOnExceed} is true:
* - Checks if the current value exceeds min or max limits if defined and
* automatically sets the value to the specific limit if exceeded.
* - Excepts for an input of only zero(s):
* In case there is a minimum higher than zero,
* zero stil is allowed as input to support leading zeros.
*
* @Param event containing the actual value
*/
onInput(event: Event) {
const input = event.target as HTMLInputElement;
input.value = input.value.replace(/\D+/g, '');
// if filling and trimming is not desired, return
if (!this.fillLimitsOnExceed()) {
return;
}
const asNumber = Number.parseInt(input.value);
// set as min if value is lower
// nevertheless allow leading zero in case min is greater than 0 (for some reason)
const courseMin = this.courseMin();
if (courseMin && asNumber < courseMin && asNumber !== 0) {
input.value = courseMin.toString();
}
// set as max if value is higher
const courseMax = this.courseMax();
if (courseMax && asNumber > courseMax) {
input.value = courseMax.toString();
}
}
/**
* Event happening when any key is pressed down.
* Checks for Esc or Enter has been hit.
* If so causes a blur.
* @Param event containing the pressed key
*/
onKeyDown(event: KeyboardEvent) {
if (event.key === 'Escape' || event.key === 'Enter') {
(event.target as HTMLElement).blur();
}
}
/**
* Event happening when focus gets lost.
*
* In case a max limit is defined leading zeros may be add or trimmed.
* The desired digit-count is based on the max value if defined.
* If so checks if leading zeros are missing and automatically adds them.
* If there are already leading zeros and they are too many, they will be cut.
* @Param event containing the pressed key
*/
onBlur(event: FocusEvent) {
const courseMax = this.courseMax();
if (courseMax) {
// add leading zeros, fill til length of max
const input = event.target as HTMLInputElement;
input.value = input.value.replace(/^0+/, '');
input.value = input.value.padStart(courseMax.toString().length, '0');
}
}
}
HTML Part
<calcite-input
label="Kurs (in Grad)"
i18n-label="...course"
title="Kurs (in Grad)" i18n-title="...course"
[value]="courseInput()" (calciteInputChange)="calciteInputNumberChange.emit($event)"
(beforeinput)="onBeforeInput($event)"
(paste)="onPaste($event)"
(input)="onInput($event)"
(keydown)="onKeyDown($event)"
(blur)="onBlur($event)"
[status] = "validityCourse()" [validationMessage]="validityCourseMessage()"
validationIcon = "exclamation-mark-triangle" />
Integrating/Calling
<my-leading-zero-input
[courseInput]="course()" [courseMin]="courseRange.min" [courseMax]="courseRange.max"
[validityCourse] = "validityCourse()" [validityCourseMessage]="validityCourseMessage()"
(calciteInputNumberChange)="refreshNumberChanged($event, course)"/>
Hi there,
You can contact esri support services through one of the means on their page - https://support.esri.com/en-us/contact
unfortunately this does not work
"Sorry, only Authorized Callers can chat and create a support case"
So, may you can do yourself.