赞
踩
项目中没有维护这个对应关系的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');
- });
- }));
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。