Skip to content

Commit a4bdf73

Browse files
spike-rabbitspliffone
authored andcommitted
refactor(phone-number): convert phoneNumber and selectedCountry to signals
This is a preparation so we can convert other fields to computed later in a follow-up PR.
1 parent 8673c4c commit a4bdf73

2 files changed

Lines changed: 64 additions & 41 deletions

File tree

projects/element-ng/phone-number/si-phone-number-input.component.html

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
[attr.aria-labelledby]="id() + '-aria-label ' + id() + '-value'"
1818
[attr.aria-expanded]="open"
1919
[options]="countryList()"
20-
[value]="selectedCountry"
20+
[value]="selectedCountry()"
2121
[tabindex]="disabled() ? '-1' : '0'"
2222
[attr.aria-controls]="id() + '-listbox'"
2323
(valueChange)="countryInput($event)"
@@ -30,9 +30,9 @@
3030
}}</span>
3131
<span
3232
aria-hidden="true"
33-
[class]="`fi fi-${(selectedCountry?.isoCode | lowercase) ?? 'xx'}`"
33+
[class]="`fi fi-${(selectedCountry()?.isoCode | lowercase) ?? 'xx'}`"
3434
></span>
35-
@if (selectedCountry) {
35+
@if (selectedCountry(); as selectedCountry) {
3636
<span class="si-body ms-4" [id]="id() + '-value'">
3737
<span class="visually-hidden">{{ selectedCountry.name }}</span>
3838
+{{ selectedCountry.countryCode }}

projects/element-ng/phone-number/si-phone-number-input.component.ts

Lines changed: 61 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ export class SiPhoneNumberInputComponent
191191
readonly errormessageId = input(`${this.id()}-errormessage`);
192192

193193
protected readonly phoneInput = viewChild.required<ElementRef<HTMLInputElement>>('phoneInput');
194-
protected selectedCountry?: CountryInfo;
194+
protected readonly selectedCountry = signal<CountryInfo | undefined>(undefined);
195195
protected placeholder = '';
196196
protected readonly countryFocused = signal(false);
197197
protected open = false;
@@ -221,7 +221,14 @@ export class SiPhoneNumberInputComponent
221221
);
222222
private readonly disabledNgControl = signal(false);
223223
private isValidNumber = true;
224-
private phoneNumber?: PhoneNumber;
224+
private readonly phoneNumber = signal<PhoneNumber | undefined>(undefined, {
225+
equal: (a, b) => {
226+
if (!a || !b) {
227+
return !a && !b;
228+
}
229+
return this.phoneUtil.isNumberMatch(a, b) === PhoneNumberUtil.MatchType.EXACT_MATCH;
230+
}
231+
});
225232
private onChange: (val: string) => void = () => {};
226233
private onTouched: () => void = () => {};
227234

@@ -233,8 +240,9 @@ export class SiPhoneNumberInputComponent
233240

234241
/** @internal */
235242
writeValue(value: string | undefined): void {
236-
this.phoneNumber = this.parseNumber(value);
237-
if (this.phoneNumber) {
243+
const phoneNumber = this.parseNumber(value);
244+
this.phoneNumber.set(phoneNumber);
245+
if (phoneNumber) {
238246
this.writeValueToInput();
239247
this.country.set(this.getRegionCode());
240248
} else {
@@ -268,13 +276,14 @@ export class SiPhoneNumberInputComponent
268276
}
269277

270278
this.isValidNumber = false;
271-
if (!this.phoneNumber || !this.phoneUtil.isValidNumber(this.phoneNumber)) {
279+
const phoneNumber = this.phoneNumber();
280+
if (!phoneNumber || !this.phoneUtil.isValidNumber(phoneNumber)) {
272281
return {
273282
invalidPhoneNumberFormat: true
274283
};
275284
}
276285

277-
if (!this.countryList().some(c => c.value.isoCode === this.selectedCountry!.isoCode)) {
286+
if (!this.countryList().some(c => c.value.isoCode === this.selectedCountry()!.isoCode)) {
278287
return {
279288
notSupportedPhoneNumberCountry: true
280289
};
@@ -286,25 +295,26 @@ export class SiPhoneNumberInputComponent
286295

287296
protected input(): void {
288297
const rawNumber = this.phoneInput().nativeElement.value;
289-
this.phoneNumber = this.parseNumber(rawNumber);
298+
const phoneNumber = this.parseNumber(rawNumber);
299+
this.phoneNumber.set(phoneNumber);
290300

291-
if (this.phoneNumber) {
301+
if (phoneNumber) {
292302
const regionCode = this.getRegionCode();
293303
let countryInfo = this.countryList().find(
294304
country => regionCode === country.value.isoCode
295305
)?.value;
296306
if (!countryInfo && regionCode) {
297307
countryInfo = {
298308
name: this.getCountryName(regionCode),
299-
countryCode: this.phoneNumber.getCountryCode()!,
309+
countryCode: phoneNumber.getCountryCode()!,
300310
isoCode: regionCode
301311
};
302312
}
303-
if (countryInfo && this.selectedCountry?.isoCode !== countryInfo.isoCode) {
304-
this.selectedCountry = countryInfo;
313+
if (countryInfo && this.selectedCountry()?.isoCode !== countryInfo.isoCode) {
314+
this.selectedCountry.set(countryInfo);
305315
}
306316
} else if (rawNumber.trim().startsWith('+')) {
307-
this.selectedCountry = undefined;
317+
this.selectedCountry.set(undefined);
308318
}
309319

310320
this.handleChange();
@@ -315,14 +325,14 @@ export class SiPhoneNumberInputComponent
315325
this.onTouched();
316326
this.writeValueToInput();
317327
this.valueChange.emit({
318-
country: this.selectedCountry,
328+
country: this.selectedCountry(),
319329
phoneNumber: this.formatPhoneNumber(PhoneNumberFormat.INTERNATIONAL),
320330
isValid: this.isValidNumber
321331
});
322332
}
323333

324334
protected countryInput(num: CountryInfo): void {
325-
this.selectedCountry = num;
335+
this.selectedCountry.set(num);
326336
this.updatePlaceholder();
327337
this.refreshValueAfterCountryChange();
328338
this.handleChange();
@@ -342,19 +352,19 @@ export class SiPhoneNumberInputComponent
342352

343353
private writeCountry(): void {
344354
const currentCountry = this.country()!;
345-
this.selectedCountry = this.countryList().find(
346-
country => country.value.isoCode === currentCountry
347-
)?.value;
348-
if (!this.selectedCountry) {
355+
this.selectedCountry.set(
356+
this.countryList().find(country => country.value.isoCode === currentCountry)?.value
357+
);
358+
if (!this.selectedCountry()) {
349359
const countryCode = this.phoneUtil.getCountryCodeForRegion(
350360
currentCountry ?? this.defaultCountry() ?? 'XX'
351361
);
352362
if (countryCode) {
353-
this.selectedCountry = {
363+
this.selectedCountry.set({
354364
isoCode: currentCountry,
355365
countryCode,
356366
name: this.getCountryName(currentCountry)
357-
};
367+
});
358368
}
359369
}
360370
this.updatePlaceholder();
@@ -371,10 +381,11 @@ export class SiPhoneNumberInputComponent
371381
}
372382

373383
private updatePlaceholder(): void {
374-
if (this.selectedCountry) {
384+
const selectedCountry = this.selectedCountry();
385+
if (selectedCountry) {
375386
this.placeholder = this.phoneUtil
376387
.format(
377-
this.phoneUtil.getExampleNumber(this.selectedCountry.isoCode),
388+
this.phoneUtil.getExampleNumber(selectedCountry.isoCode),
378389
PhoneNumberFormat.NATIONAL
379390
)
380391
.replace(/^0/, '');
@@ -385,7 +396,7 @@ export class SiPhoneNumberInputComponent
385396
try {
386397
let regionCodeForParsing: string | undefined;
387398
if (!rawNumber?.trim().startsWith('+')) {
388-
regionCodeForParsing = this.selectedCountry?.isoCode;
399+
regionCodeForParsing = this.selectedCountry()?.isoCode;
389400
}
390401
return this.phoneUtil.parse(rawNumber, regionCodeForParsing);
391402
} catch (e) {
@@ -399,49 +410,52 @@ export class SiPhoneNumberInputComponent
399410
* This Method fakes a complete number to force PhoneUtil returning a proper region code.
400411
*/
401412
private getRegionCode(): string | undefined {
402-
if (this.phoneNumber) {
403-
const regionCode = this.phoneUtil.getRegionCodeForNumber(this.phoneNumber);
413+
const phoneNumber = this.phoneNumber();
414+
if (phoneNumber) {
415+
const regionCode = this.phoneUtil.getRegionCodeForNumber(phoneNumber);
404416
if (regionCode) {
405417
return regionCode;
406418
}
407419

408-
const nationalNumber = this.phoneNumber.getNationalNumber() + '';
420+
const nationalNumber = phoneNumber.getNationalNumber() + '';
409421
if (
410422
// USA, CANADA, ...
411-
(this.phoneNumber.getCountryCode() === 1 && nationalNumber.length >= 3) ||
423+
(phoneNumber.getCountryCode() === 1 && nationalNumber.length >= 3) ||
412424
// UK, ...
413-
(this.phoneNumber.getCountryCode() === 44 && nationalNumber.length >= 4)
425+
(phoneNumber.getCountryCode() === 44 && nationalNumber.length >= 4)
414426
) {
415427
return this.phoneUtil.getRegionCodeForNumber(
416428
this.phoneUtil.parse(
417429
'+' +
418-
this.phoneNumber.getCountryCode() +
430+
phoneNumber.getCountryCode() +
419431
nationalNumber +
420432
new Array(10 - nationalNumber.length).fill(5).join('')
421433
)
422434
);
423435
}
424436

425-
return this.phoneUtil.getRegionCodeForCountryCode(this.phoneNumber.getCountryCode()!);
437+
return this.phoneUtil.getRegionCodeForCountryCode(phoneNumber.getCountryCode()!);
426438
}
427439

428440
return undefined;
429441
}
430442

431443
private formatPhoneNumber(format: PhoneNumberFormat): string | undefined {
432-
if (this.phoneNumber) {
433-
return this.phoneUtil.format(this.phoneNumber, format);
444+
const phoneNumber = this.phoneNumber();
445+
if (phoneNumber) {
446+
return this.phoneUtil.format(phoneNumber, format);
434447
}
435448

436449
return undefined;
437450
}
438451

439452
private handleChange(): void {
440-
if (this.selectedCountry && this.country() !== this.selectedCountry?.isoCode) {
441-
this.country.set(this.selectedCountry?.isoCode);
453+
const selectedCountry = this.selectedCountry();
454+
if (selectedCountry && this.country() !== selectedCountry.isoCode) {
455+
this.country.set(selectedCountry.isoCode);
442456
}
443457

444-
if (this.phoneNumber) {
458+
if (this.phoneNumber()) {
445459
this.onChange(this.formatPhoneNumber(PhoneNumberFormat.INTERNATIONAL)!);
446460
} else {
447461
this.onChange('');
@@ -455,14 +469,23 @@ export class SiPhoneNumberInputComponent
455469
* Format and update input text or clear input text if the input value is undefined.
456470
*/
457471
private writeValueToInput(): void {
458-
if (this.phoneNumber) {
472+
if (this.phoneNumber()) {
459473
this.writeTextToInput(this.formatPhoneNumber(PhoneNumberFormat.NATIONAL)!.replace(/^0/, ''));
460474
}
461475
}
462476

463477
private refreshValueAfterCountryChange(): void {
464-
if (this.selectedCountry) {
465-
this.phoneNumber?.setCountryCode(this.selectedCountry?.countryCode);
478+
const selectedCountry = this.selectedCountry();
479+
if (selectedCountry) {
480+
this.phoneNumber.update(current => {
481+
if (!current) {
482+
return undefined;
483+
}
484+
// TODO: Remove any once https://github.com/DefinitelyTyped/DefinitelyTyped/pull/75189 is merged/released
485+
const phoneNumber = (current as any)?.clone();
486+
phoneNumber.setCountryCode(selectedCountry.countryCode);
487+
return phoneNumber;
488+
});
466489
this.writeValueToInput();
467490
}
468491
}

0 commit comments

Comments
 (0)