如何将子组件与父组件一起使用 - Angular

问题描述 投票:0回答:1

我创建了这个名为country-flag-dropdown.ts和html的组件 成分:

import {Component, OnInit, Input, Output, EventEmitter} from "@angular/core";
import {FormGroup, FormControl} from "@angular/forms";
import {CountryDataService} from "projects/pitxpress-admin-app/src/app/pitxpress/api-services/country/country-data.service";
import {ICountry} from "projects/pitxpress-admin-app/src/app/pitxpress/api-services/country/models/country.interface";
import {PhoneNumberUtil, PhoneNumberFormat} from "google-libphonenumber";

/**
 * Country flag dropdown component.
 */
@Component({
    selector: "app-country-flag-dropdown",
    templateUrl: "./country-flag-dropdown.component.html",
    styleUrls: ["./country-flag-dropdown.component.scss"],
})
export class CountryFlagDropdownComponent implements OnInit {
    /**
     * Form group for the component.
     */
    @Input() public form!: FormGroup;

    /**
     * Control name for the phone number input.
     */
    @Input() public controlName!: string;

    /**
     * Event emitter for phone number changes.
     */
    @Output() public phoneNumberChange = new EventEmitter<string>();

    /**
     * List of countries.
     */
    public countries: ICountry[] = [];

    /**
     * Selected dial code.
     */
    public selectedDialCode: string = "";

    /**
     * Selected country.
     */
    public selectedCountry: ICountry | undefined;

    /**
     * Country control form control.
     */
    public countryControl = new FormControl();

    /**
     * Phone number utility instance.
     */
    private phoneNumberUtil = PhoneNumberUtil.getInstance();

    /**
     * Form group for country control.
     */
    public countryForm = new FormGroup({
        countryControl: new FormControl(),
    });

    /**
     * Constructor.
     * @param countryDataService Country data service instance.
     */
    constructor(private countryDataService: CountryDataService) {}

    /**
     * Initialize component.
     */
    public ngOnInit(): void {
        // Initialize countries data and set initial country to United States if it exists
        this.countryDataService.getCountries().subscribe((countries) => {
            this.countries = countries;
            this.initInitialCountry();
            this.checkExistingPhoneNumber();
        });

        // Subscribe to country control value changes
        this.countryControl.valueChanges.subscribe((value) => {
            this.onCountryChange(value);
        });
    }

    /**
     * Initialize initial country to United States if it exists.
     */
    private initInitialCountry(): void {
        const usa = this.countries.find((country) => country.isoCode2 === "US");
        if (usa) {
            this.selectedCountry = usa;
            this.selectedDialCode = usa.dialCodes[0];
            this.countryControl.setValue(this.selectedDialCode, {emitEvent: false});
            this.form.get(this.controlName)?.setValue(this.selectedDialCode);
        }
    }

    /**
     * Check if there's an existing phone number.
     */
    private checkExistingPhoneNumber(): void {
        const phoneNumber = this.form.get(this.controlName)?.value;
        if (phoneNumber) {
            this.selectedDialCode = this.extractDialCode(phoneNumber);
            this.form.get(this.controlName)?.setValue(phoneNumber);
            const iso2Code = this.getIso2CodeFromPhoneNumber(phoneNumber);
            if (iso2Code) {
                this.updateCountrySelectionByIsoCode(iso2Code);
            }
        }
    }

    /**
     * Handle country change event.
     * @param dialCode Selected dial code.
     */
    public onCountryChange(dialCode: string): void {
        const selectedCountry = this.countries.find((country) => country.dialCodes.includes(dialCode));
        if (selectedCountry) {
            this.selectedDialCode = dialCode;
            this.selectedCountry = selectedCountry;
            const inputValue = this.form.get(this.controlName)?.value.replace(this.selectedDialCode, "");
            this.form.get(this.controlName)?.setValue(this.selectedDialCode + inputValue);
            this.phoneNumberChange.emit(this.selectedDialCode + inputValue);
        } else {
            // If the country is selected from the dropdown, update the selectedCountry and selectedDialCode
            const selectedCountry = this.countries.find((country) => country.dialCodes[0] === dialCode);
            if (selectedCountry) {
                this.selectedCountry = selectedCountry;
                this.selectedDialCode = dialCode;
            }
        }
    }

    /**
     * Handle phone number input event.
     * @param event Input event.
     */
    public onPhoneNumberInput(event: any): void {
        const inputElement = event.target as HTMLInputElement;
        const inputValue = inputElement.value.trim();
        this.formatPhoneNumber(inputValue);
        const countryCode = this.getCountryCodeFromPhoneNumber(inputValue);
        if (countryCode) {
            const isoCode = this.phoneNumberUtil.getRegionCodeForCountryCode(parseInt(countryCode));
            if (isoCode) {
                this.updateCountrySelectionByIsoCode(isoCode);
            }
        }
        // Update the phone number value in the form
        this.form.get(this.controlName)?.setValue(inputValue);
        // Emit the phone number change event
        this.phoneNumberChange.emit(inputValue);
    }

    /**
     * Extract dial code from phone number.
     * @param phoneNumber Phone number.
     * @returns Dial code.
     */
    private extractDialCode(phoneNumber: string): string {
        for (const country of this.countries) {
            if (Array.isArray(country.dialCodes)) {
                for (const dialCode of country.dialCodes) {
                    if (phoneNumber.startsWith(dialCode)) {
                        return dialCode;
                    }
                }
            }
        }
        return "";
    }

    /**
     * Update country selection by ISO code.
     * @param isoCode ISO code.
     */
    private updateCountrySelectionByIsoCode(isoCode: string): void {
        const selectedCountry = this.countries.find((country) => country.slug === isoCode);
        if (selectedCountry) {
            console.log("Updating country selection by ISO code:", selectedCountry);
            this.selectedCountry = selectedCountry;
            this.selectedDialCode = selectedCountry.dialCodes[0];
            this.countryControl.setValue(this.selectedDialCode, {emitEvent: false});
        }
    }

    /**
     * Get ISO 2 code from phone number.
     * @param phoneNumber Phone number.
     * @returns ISO 2 code.
     */
    private getIso2CodeFromPhoneNumber(phoneNumber: string): string {
        try {
            const number = this.phoneNumberUtil.parse(phoneNumber, ""); // Use the default region for parsing
            return this.phoneNumberUtil.getRegionCodeForNumber(number) || "";
        } catch (error) {
            console.error("Error parsing phone number:", error);
            return "";
        }
    }

    /**
     * Get country code from phone number.
     * @param phoneNumber Phone number.
     * @returns Country code.
     */
    private getCountryCodeFromPhoneNumber(phoneNumber: string): string {
        try {
            const number = this.phoneNumberUtil.parse(phoneNumber, "");
            return number?.getCountryCode()?.toString() || "";
        } catch (error) {
            console.error("Error parsing phone number:", error);
            return "";
        }
    }

    /**
     * Format phone number.
     * @param phoneNumber Phone number.
     * @returns Formatted phone number.
     */
    public formatPhoneNumber(phoneNumber: string): string {
        try {
            const number = this.phoneNumberUtil.parse(phoneNumber, this.selectedCountry?.isoCode2);
            return this.phoneNumberUtil.format(number, PhoneNumberFormat.INTERNATIONAL);
        } catch (error) {
            console.error("Error formatting phone number:", error);
            return phoneNumber;
        }
    }
}

HTML:

<div class="input-group country-dropdown-group" [formGroup]="form">
    <form [formGroup]="countryForm">
    <p-dropdown 
        [options]="countries" 
        optionLabel="name" 
        optionValue="dialCodes[0]" 
        appendTo="body"
        [filter]="true" 
        filterBy="name"
        formControlName="countryControl"
        class="p-inputgroup-addon country-flag-selector">
  
        <ng-template let-country pTemplate="selectedItem">
            <div class="country-selector-item">
        <img [src]="'assets/flags/' + selectedCountry?.slug + '.svg'" class="country-flag" />
                <span>{{ selectedCountry?.slug }}</span>
            </div>
        </ng-template>
  
        <ng-template let-country pTemplate="item">
            <div class="country-selector-item">
                <img [src]="'assets/flags/' + country.slug + '.svg'" class="country-flag" />
                <span>{{ country.name }}</span>
            </div>
        </ng-template>
    </p-dropdown>
    </form>
    <input 
        type="text" 
        pInputText 
        [formControlName]="controlName" 
        maxlength="15" 
        placeholder="Phone Number" 
        class="form-control" 
        (input)="onPhoneNumberInput($event)" 
        [value]="formatPhoneNumber(form.get(controlName)?.value)"/>        
</div>

我的目标是创建一个国家/地区国旗下拉列表,用户可以在其中选择国旗或输入电话号码,然后就会显示国旗。但使用当前代码,我无法从下拉列表中选择标志,并且它不会反映在用户界面中。

我想我对它的问题有一个大概的了解。我有一个 @Input 表单,但我也有一个 CountyForm。这两种形式似乎都是在不同的地方访问的,这有点令人困惑。 @Input 表单的想法是尝试添加到父组件上的现有表单。在这一行中

<img [src]="'assets/flags/' + selectedCountry?.slug + '.svg'" class="country-flag" />
如果我将 selectedCountry?.slug 更改为国家/地区,我可以从下拉列表中选择标志,它将反映在 UI 上,但当我输入时我无法检测到标志一个数字,因此当我需要两种功能时,它会剥夺一种功能。我只是好奇我怎样才能做到这一点?

angular forms angular-reactive-forms formgroups form-control
1个回答
0
投票

您可以尝试在您的 CountryFlagDropdownComponent OnInit 中添加此订阅:

this.form.get(this.controlName)?.valueChanges.subscribe((value) => {
   this.onPhoneNumberInput(value);
});

onPhoneNumberInput 方法将电话号码作为输入并更新选定的国家/地区,onCountryChange 方法根据下拉列表中选定的国家/地区更新电话号码。

public onPhoneNumberInput(phoneNumber: string): void {
    this.formatPhoneNumber(phoneNumber);
    const countryCode = this.getCountryCodeFromPhoneNumber(phoneNumber);
    if (countryCode) {
      const isoCode = this.phoneNumberUtil.getRegionCodeForCountryCode(parseInt(countryCode));
      if (isoCode) {
        this.updateCountrySelectionByIsoCode(isoCode);
      }
    }
    this.form.get(this.controlName)?.setValue(phoneNumber);
    this.phoneNumberChange.emit(phoneNumber);
  }
public onCountryChange(dialCode: string): void {
    const selectedCountry = this.countries.find((country) => country.dialCodes.includes(dialCode));
    if (selectedCountry) {
      this.selectedDialCode = dialCode;
      this.selectedCountry = selectedCountry;
      const inputValue = this.form.get(this.controlName)?.value.replace(this.selectedDialCode, "");
      this.form.get(this.controlName)?.setValue(this.selectedDialCode + inputValue);
      this.phoneNumberChange.emit(this.selectedDialCode + inputValue);
    }
  }

最后你应该用

(input)="onPhoneNumberInput($event.target.value)"

更新 html
© www.soinside.com 2019 - 2024. All rights reserved.