如何根据美国的邮编带出对应的州呢

项目中没有维护这个对应关系的API,所以我google到了Google map的API,只需要发get请求发送邮编即可。api地址是
http://maps.googleapis.com/maps/api/geocode/json?address=xxx,这里的address就是接受的邮编。
所以现在需求是:用户输入邮政编码后请求该API得到对应的州和city,然后渲染到对应的字段中。
create-client.component.html

<!-- client form -->
<div animated fadeIn>
  <form class="client-form" class="form-horizontal" [formGroup]="orgForm">
    <div class="row">
      <div class="col-md-10">
        <div class="card">
          <div class="card-header"><strong>Account Information</strong></div>
          <div class="card-body">
            
            <div class="row">
              <label for="country"
                     class="col-md-3 form-control-label required-label"><span class="asterisk">*</span>Country:</label>
              <div class="col-md-9">
                <select style="height: 34px;" class="col-md-5 form-control" id="country" name="country" formControlName="country">
                  <option *ngFor="let country of countries"
                          [value]="country">
                    {{ country }}
                  </option>
                </select>
              </div>
            </div>

            <div class="row">
              <label for="postal"
                     class="col-md-3 form-control-label required-label"><span class="asterisk">*</span>Postal Code:</label>
              <div class="col-md-9">
                <input id="postal" name="postal"
                       class="col-md-5 form-control"
                       formControlName="postal"
                       placeholder="Enter Client Postal Code" (change)="onPostalChange($event)" >
                <span class="col-md-5 login-error-alert" *ngIf="this.isFormInputInvalid('postal') &&
                this.orgForm.get('postal').hasError('required')">Postal Code is required</span>

                <span class="col-md-5 login-error-alert" *ngIf="this.isFormInputInvalid('postal') &&
                !this.orgForm.get('postal').hasError('required') &&
                this.orgForm.get('postal').hasError('pattern')">Postal Code must be a 5 digit number</span>

                <span class="col-md-5 login-error-alert" *ngIf="this.isPostalCodeInvalid &&
                !this.orgForm.get('postal').hasError('pattern') &&
                !this.orgForm.get('postal').hasError('required')">Postal Code is not available</span>
              </div>
            </div>

            <div class="row">
              <label for="city"
                     class="col-md-3 form-control-label required-label"><span class="asterisk">*</span>City:</label>
              <div class="col-md-9">
                <input id="city" name="city" [(ngModel)]="cityName"
                       class="col-md-5 form-control"
                       formControlName="city"
                       placeholder="Enter Client City">
                <span class="col-md-5 login-error-alert" *ngIf="this.isFormInputInvalid('city') &&
                this.orgForm.get('city').hasError('required')">City is required</span>
                <span class="col-md-5 login-error-alert" *ngIf="this.isFormInputInvalid('city') &&
                this.orgForm.get('city').hasError('pattern')">City cannot exceed 40 characters</span>
              </div>
            </div>

            <div class="row">
              <label for="state"
                class="col-md-3 form-control-label required-label"><span class="asterisk">*</span>State:</label>
              <div class="col-md-9">
                <select style="height: 34px;" class="col-md-5 form-control" id="state" formControlName="state"
                         [(ngModel)]="stateAbbreviation" (ngModelChange)="onStateChange($event)">
                  <option *ngFor="let state of stateItems"
                          [value]="state">
                    {{ state }}
                  </option>
                </select>
                <div *ngIf="orgForm.get('state').touched || orgForm.get('state').dirty">
                  <span class="col-md-5 login-error-alert" *ngIf="this.isFormInputInvalid('state') &&
                         this.orgForm.get('state').hasError('required')">State is required</span>
                  <span class="col-md-5 login-error-alert" *ngIf="this.isStateValueMismatchWithPostalCode"> Postal Code and State Mismatch</span>
                </div>
              </div>
            </div>
      </div>
    </div>
    <div class="modal-footer col-md-10">
      <div class="col-md-9">
        <div class="col-md-5">
          <button type="button" class="btn btn-dark-rs6 col-md-5" id="reset-button-modal"
                  (click)="orgForm.reset()" style="margin-left: -24px">Reset
          </button>
          <button type="submit" class="btn btn-green-rs6 col-md-5" id="submit-button-modal"
                  [disabled]="!orgForm.valid || this.isStateValueMismatchWithPostalCode || isPostalCodeInvalid" (click)="onSubmit()"
                  style="float: right; margin-right: -15px">Create
          </button>
        </div>
      </div>
    </div>
  </form>
</div>

子类:create-client.component.ts
 

import { Component } from '@angular/core';
import { FormBuilder, Validators } from '@angular/forms';
import { ActivatedRoute, Params, Router } from '@angular/router';
import { Location } from '@angular/common';
import { Account } from '../../model/account.model';
import { PaymentTermList } from '../../model/payment-term-list.model';
import { PricingPackage } from '../../model/pricing-package.model';
import { Processor } from '../../model/processor.model';
import { RequestBilling } from '../../model/request-billing.model';
import { User } from '../../model/user.model';
import { DialogService } from '../../service/dialog.service';
import { OrganizationService } from '../../service/organization.service';
import { CreateOrganizationComponent } from '../create-organization/create-organization.component';
import {Regex} from '../../model/regex.model';
import { OrganizationType } from '../../model/organization-type.model';
import {UtilService} from '../../service/util.service';
import {Constants} from '../../utility/constants';
import {Utility} from '../../utility/utility';

@Component({
  selector: 'app-create-client',
  templateUrl: 'create-client.component.html'
})

export class CreateClientComponent extends CreateOrganizationComponent {
  account: Account = {
    soldBy: '', ownedBy: '', address: {
      address1: '', address2: '', city: '', state: '', country: '', postal: ''
    }, emailAddress: '', locale: {language: {cultureName: ''}},
    processorList: [{name: ''}]
  };
 
  countries: string[] = [];
  stateItems: string[] = [];

  constructor(private fb: FormBuilder, route: ActivatedRoute, dialogService: DialogService,
              organizationService: OrganizationService, utilService: UtilService,
              router: Router, location: Location, regex: Regex) {
    super(route, dialogService, organizationService, router, location, regex, utilService);
    this.createForm();
    this.title = 'Client Creation';
    this.route.queryParams.subscribe((params: Params) => {
      this.clientId = params['clientId'];
      this.parentOrganizationId = params['parentOrganizationId'];
    });
    this.initProcessorList();
    this.organizationService.getPaymentTerms().subscribe(
      (paymentTermList: PaymentTermList) => this.paymentTermList = paymentTermList);

    this.utilService.getCountries().subscribe(
      (countries: string[] ) => this.countries = countries);

    this.utilService.getStates().subscribe(
      (states: string[] ) => this.stateItems = states);
  }


  onSubmit() {
    this.updateModel();
    super.onSubmit(OrganizationType.Client);
  }

  updateModel() {
    
    this.model.account.address.city = this.orgForm.get('city').value;
    this.model.account.address.state = this.orgForm.get('state').value;
    this.model.account.address.postal = this.orgForm.get('postal').value;
    this.model.account.address.country = Utility.convertSpecificCountryNameInShort(this.orgForm.get('country').value);

    this.model.account.processorList = [];
    for (const processor of this.processors) {
      if (processor['checked']) {
        const p: Processor = {name: processor['name']};
        this.model.account.processorList.push(p);
      }
    }
  }

  onPostalChange($event) {
    this.onPostalChanges(this.orgForm.get('postal').value);
  }

  private createForm() {
    this.orgForm = this.fb.group({
      
      state: ['', [Validators.required, Validators.pattern(this.regex.state)]],
      city: ['', [Validators.required, Validators.pattern(this.regex.city)]],
      country: ['', [Validators.required]],
      postal: ['', [Validators.required, Validators.pattern(this.regex.postalCode)]],
    );
  }
}

父类:create-organization.component.ts

 

import { HttpErrorResponse, HttpResponse, HttpResponseBase } from '@angular/common/http';
import { Component, ViewChild, ElementRef } from '@angular/core';
import { AbstractControl, FormGroup } from '@angular/forms';
import { ValidationErrors } from '@angular/forms/src/directives/validators';
import { ActivatedRoute, Params, Router} from '@angular/router';
import { Location } from '@angular/common';
import { Observable } from 'rxjs/Observable';
import { Organization } from '../../model/organization.model';
import { Regex } from '../../model/regex.model';
import { DialogService } from '../../service/dialog.service';
import { OrganizationService } from '../../service/organization.service';
import { Utility } from '../../utility/utility';
import { OrganizationType } from '../../model/organization-type.model';
import { UtilService } from '../../service/util.service';

@Component({
  selector: 'app-create-organization',
  templateUrl: 'create-organization.component.html'
})
export class CreateOrganizationComponent {
  parentOrganizationUserName = '';
  parentOrganizationId: number;
  orgForm: FormGroup;


  stateAbbreviationFirstValue = '';
  isFirstTimeToChoose = true;
  stateAbbreviation = '';
  isPostalCodeInvalid = false;
  isStateValueMismatchWithPostalCode = false;
  cityName = '';

  constructor(public route: ActivatedRoute, public dialogService: DialogService, public organizationService: OrganizationService,
              private router: Router, public location: Location, public regex: Regex, public utilService: UtilService) {
  }

  onSubmit(orgType: any) {
    let parentOrgId = '';
    if (this.parentOrganizationId) {
      parentOrgId = this.parentOrganizationId.toString();
    }
    this.organizationService.createChildOrganization(this.parentOrganizationUserName, parentOrgId, this.model)
      .subscribe((response: HttpResponseBase) => {
        if (response instanceof HttpResponse) {
          this.createdOrgId = response.body['organizationId'];
          this.resResult = !response.body['customMessage'];
          this.openDialog(this.resResult, orgType, response.body['customMessage']);
        } else if (response instanceof HttpErrorResponse) {
          this.resErrorResult = response.error['text'];
          this.openDialog(false, orgType, this.resErrorResult);
        }
      }, (error) => {
        this.openDialog(false, orgType, '');
      });
  }

  isFormInputInvalid(input: string): boolean {
    return Utility.isFormInputInvalid(input, this.orgForm);
  }

  isUserNameAvailable(control: AbstractControl): Observable<ValidationErrors | null> {
    return this.organizationService.getUserNameAvailability(control.value).map((res: any) => res.isUserNameAvailable ? null : res);
  }


  /**
   * Get the state abbreviation with the postal code from google API and judged whether is postal code is valid or not.
   * @param postalCode postal code.
   */
  onPostalChanges(postalCode: string) {
    this.isFirstTimeToChoose = true;
    if (postalCode) {
      this.utilService.getAddressInfoWithPostalCode(postalCode).subscribe(res => {
        const address = Utility.filterStateWithPostalCodeFromGoogleApi(res);
        if (address && address.state) {
          this.isPostalCodeInvalid = false;
          this.stateAbbreviation = address.state;
          this.cityName = address.city;
          this.isStateValueMismatchWithPostalCode = false;
        } else {
          this.isPostalCodeInvalid = true;
        }
      });
    }
  }

  /**
   * When changed the state select value then will call this method,it used to confirm whether
   * the state value is match with the postal code or not.
   * @param stateAbbreviationFirstValue the abbreviation of the firstly selected state value.
   * @param stateAbbreviationNewValue the abbreviation of the now selected state value.
   */
  isStateAndPostalCodeValid(stateAbbreviationNewValue: string, stateAbbreviationFirstValue: string) {
    if (stateAbbreviationNewValue !== undefined && stateAbbreviationFirstValue !== undefined) {
      if (stateAbbreviationFirstValue.match(stateAbbreviationNewValue)) {
        this.isStateValueMismatchWithPostalCode = false;
      } else {
        this.isStateValueMismatchWithPostalCode = true;
      }
    }
  }

  /**
   * change event on state field
   * save the first value of state field,and judge whether the postal code and state is match or not.
   */
  onStateChange($event) {
    if (this.isFirstTimeToChoose === true) {
      this.stateAbbreviationFirstValue = this.stateAbbreviation;
      this.isFirstTimeToChoose = false;
    }
    this.isStateAndPostalCodeValid(this.stateAbbreviation, this.stateAbbreviationFirstValue);
  }
}

Utility.ts,解析谷歌api返回的json
 

/**
   * Filter the results of requesting google api, retain the state's abbreviation and the city name.
   * @param res the results from calling the google API.
   * @returns Address including the abbreviation of State and the city name.
   */
  static filterStateWithPostalCodeFromGoogleApi(res: any): Address {
    const address: Address = {};
    if (res['results'].length === 0) {
      return res[0];
    } else {
      const stateNames = res['results'][0]['address_components'].filter(resp => resp['types'][0] === 'administrative_area_level_1')
        .map(re => re['short_name']);
      const cityNames = res['results'][0]['address_components'].filter(resp => resp['types'][0] === 'locality')
        .map(re => re['long_name']);

      address.state = stateNames[0];
      address.city = cityNames[0];
      return address;
    }
  }

 

测试点击事件ngModelChange

it('should go from model to change event', async(() => {
    const fixture = TestBed.createComponent(CreateMerchantComponent);
    const comp = fixture.componentInstance;
    spyOn(comp, 'onStateChange');
    comp.stateItems = states;
    comp.stateAbbreviation = states[1];
    fixture.detectChanges();
    const select = fixture.debugElement.query(By.css('#state'));
    fixture.whenStable().then(() => {
      select.nativeElement.dispatchEvent(new Event('change'));
      fixture.detectChanges();
      expect(comp.onStateChange).toHaveBeenCalledWith('AK');
    });
  }));

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值