WIP
This commit is contained in:
@@ -0,0 +1,31 @@
|
||||
<div class="row">
|
||||
|
||||
<div class="col-sm-4 col-6">
|
||||
<div class="form-group">
|
||||
<label for="zips-{{uuid}}" class="label">Zip Code:</label>
|
||||
<rbj-drop-down [name]="name+'Zip'" [ngModel]="zip" editable [source]="this.zipCodeList"
|
||||
[maskExpression]="'00000-9999'" [id]="id+'Zip'" [inputClass]="'text-left '+inputClass"
|
||||
(selectedChange)="zipCodeChanged($event);zipcodeToCounty(zip);onBlur()" [readonly]="readonly"
|
||||
[disabled]="isDisabled" [required]="required" [mustMatch]="false">
|
||||
</rbj-drop-down>
|
||||
<!-- <input type="text" nbInput fullWidth name="zips-{{uuid}}" [(ngModel)]="zip" (change)="zipCodeChanged()"> (blur)="zipCodeChanged($event);onBlur()" -->
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-5 col-12">
|
||||
<div class="form-group">
|
||||
<label for="citys-{{uuid}}" class="label">City</label>
|
||||
<input type="text" class="{{inputClass}}" nbInput fullWidth [name]="name+'City'" [(ngModel)]="city"
|
||||
[id]="id+'City'" autocomplete="off" [readonly]="readonly" (blur)="cityOrStateChanged();onBlur()"
|
||||
[disabled]="isDisabled" [required]="required" [inputLimitation]="37">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-3 col-6">
|
||||
<div class="form-group">
|
||||
<label for="states-{{uuid}}" class="label">State</label>
|
||||
<input type="text" class="{{inputClass}}" nbInput fullWidth [name]="name+'State'" [(ngModel)]="state"
|
||||
[id]="id+'State'" autocomplete="off" [mask]="'UU'" validate [invalidMsg]="'Invalid state code format'" ffMsg
|
||||
[readonly]="readonly" (blur)="cityOrStateChanged();onBlur()" [disabled]="isDisabled" [required]="required"
|
||||
disableForceFocus>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,10 @@
|
||||
@import "../../@theme/styles/themes";
|
||||
|
||||
form.ng-touched input.ng-invalid {
|
||||
border-color: nb-theme(color-danger-default);
|
||||
}
|
||||
|
||||
input[type="text"][disabled] {
|
||||
color: black;
|
||||
background-color: white;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
||||
|
||||
import { CityStateZipComponent } from './city-state-zip.component';
|
||||
|
||||
describe('CityStateZipComponent', () => {
|
||||
let component: CityStateZipComponent;
|
||||
let fixture: ComponentFixture<CityStateZipComponent>;
|
||||
|
||||
beforeEach(waitForAsync(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ CityStateZipComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(CityStateZipComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,376 @@
|
||||
import { Component, forwardRef, ViewChild, ElementRef, Input, Output, EventEmitter, Renderer2 } from '@angular/core';
|
||||
import { NG_VALUE_ACCESSOR, NG_VALIDATORS, ControlContainer, NgForm, Validator, AbstractControl, ValidationErrors } from '@angular/forms';
|
||||
import { UuidUtils } from '../../utilities/uuid-utils';
|
||||
import { DropDownOption } from '../../entity/dropDownOption';
|
||||
import { ForceFocusMsgDirective } from '../../directives/force-focus-msg/force-focus-msg.directive';
|
||||
import { AddressInfo } from '../../models/contactInfo.model';
|
||||
import { MsgBoxService } from '../../services/msg-box.service';
|
||||
import { first, takeUntil } from 'rxjs/operators';
|
||||
import { ObjectUtils } from '../../utilities/object-utils';
|
||||
import { CityStateZipService, CityInfo } from '../../services/city-state-zip.service';
|
||||
import { Subject } from 'rxjs';
|
||||
import { StringUtils } from '../../utilities/string-utils';
|
||||
import { ADIcon } from '../alert-dlg/alert-dlg.model';
|
||||
|
||||
var zipcodes = require('zipcodes-nrviens');
|
||||
|
||||
@Component({
|
||||
selector: 'city-state-zip',
|
||||
templateUrl: './city-state-zip.component.html',
|
||||
styleUrls: ['./city-state-zip.component.scss'],
|
||||
providers: [
|
||||
{
|
||||
provide: NG_VALUE_ACCESSOR,
|
||||
useExisting: forwardRef(() => CityStateZipComponent),
|
||||
multi: true
|
||||
},
|
||||
{
|
||||
provide: NG_VALIDATORS,
|
||||
useExisting: forwardRef(() => CityStateZipComponent),
|
||||
multi: true,
|
||||
},
|
||||
|
||||
],
|
||||
viewProviders: [{ provide: ControlContainer, useExisting: NgForm }]
|
||||
})
|
||||
export class CityStateZipComponent implements Validator {
|
||||
|
||||
@ViewChild('city', { static: true }) cityInput: ElementRef;
|
||||
@ViewChild('state', { static: true }) stateInput: ElementRef;
|
||||
@ViewChild('zip', { static: true }) zipInput: ElementRef;
|
||||
@ViewChild(ForceFocusMsgDirective) statePopover: ForceFocusMsgDirective;
|
||||
|
||||
private _value: AddressInfo = { city: '', state: '', zip: '', county: '' } as AddressInfo;
|
||||
private _oldValue: AddressInfo = { city: '', state: '', zip: '', county: '' } as AddressInfo;
|
||||
private _initialized: boolean;
|
||||
private _legalInput: boolean = false;
|
||||
private _readOnly: boolean;
|
||||
private _disabled: boolean;
|
||||
|
||||
private _writing: boolean = true;
|
||||
private _msgShown: boolean = false;
|
||||
private destroy$: Subject<void> = new Subject<void>();
|
||||
uuid = UuidUtils.generate();
|
||||
disabledState: boolean = false;
|
||||
required: boolean = false;
|
||||
//zipCodeList: DropDownOption[] = [];
|
||||
|
||||
private _zipCodeList: DropDownOption[] = [new DropDownOption('', '')];
|
||||
public get zipCodeList(): DropDownOption[] {
|
||||
return this._zipCodeList;
|
||||
}
|
||||
public set zipCodeList(v: DropDownOption[]) {
|
||||
if (v.length > 0) {
|
||||
if (v.length != this._zipCodeList.length || v[0].value1 != this._zipCodeList[0].value1) {
|
||||
this._zipCodeList = [new DropDownOption('', '')].concat(v);
|
||||
}
|
||||
} else {
|
||||
this._zipCodeList = [new DropDownOption('', '')];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Input() id?: string = ''
|
||||
@Input() name?: string = ''
|
||||
@Input() placeholder: string;
|
||||
@Input() inputClass: string;
|
||||
|
||||
@Input() size: string = 'medium';
|
||||
allData: any;
|
||||
|
||||
@Input()
|
||||
public set readonly(value) {
|
||||
this._readOnly = typeof value !== 'undefined' && value !== false;
|
||||
}
|
||||
|
||||
@Input()
|
||||
public set isDisabled(value) {
|
||||
this._disabled = typeof value !== 'undefined' && value !== false;
|
||||
}
|
||||
|
||||
public get isDisabled(): boolean {
|
||||
return this._disabled;
|
||||
}
|
||||
|
||||
@Input("required")
|
||||
public set input_required(value) {
|
||||
this.required = typeof value !== "undefined" && value !== false;
|
||||
}
|
||||
|
||||
|
||||
public get readonly(): boolean {
|
||||
return this._readOnly;
|
||||
}
|
||||
|
||||
|
||||
public get value(): AddressInfo {
|
||||
return this._value;
|
||||
}
|
||||
public set value(v: AddressInfo) {
|
||||
if (this._value != v) {
|
||||
this._value = v;
|
||||
this.onChange(this.value);
|
||||
}
|
||||
}
|
||||
|
||||
public get city(): string {
|
||||
|
||||
return this.value.city;
|
||||
}
|
||||
@Input() public set city(v: string) {
|
||||
if (this.value.city != v) {
|
||||
this.value.city = v;
|
||||
this.cityChange.emit(v);
|
||||
|
||||
this.onChange(this.value);
|
||||
}
|
||||
}
|
||||
|
||||
public get state(): string {
|
||||
return this.value.state
|
||||
}
|
||||
@Input() public set state(v: string) {
|
||||
if (this.value.state != v) {
|
||||
this.value.state = v;
|
||||
this.stateChange.emit(v);
|
||||
//this.writeValue(this.value);
|
||||
this.onChange(this.value);
|
||||
}
|
||||
}
|
||||
public get zip(): string {
|
||||
|
||||
return this.value.zip;
|
||||
}
|
||||
@Input() public set zip(v: string) {
|
||||
|
||||
if (this.value.zip != v) {
|
||||
this.value.zip = v;
|
||||
this.zipChange.emit(v);
|
||||
this.onChange(this.value);
|
||||
}
|
||||
}
|
||||
|
||||
public get county(): string {
|
||||
return this.value.county;
|
||||
}
|
||||
|
||||
public set county(v: string) {
|
||||
if (this.value.county != v) {
|
||||
this.value.county = v;
|
||||
this.countyChange.emit(v);
|
||||
this.onChange(this.value);
|
||||
}
|
||||
}
|
||||
|
||||
@Output() zipChange = new EventEmitter<string>()
|
||||
|
||||
@Output() stateChange = new EventEmitter<string>()
|
||||
@Output() cityChange = new EventEmitter<string>()
|
||||
@Output() countyChange = new EventEmitter<string>()
|
||||
|
||||
@Output() focus = new EventEmitter();
|
||||
@Output() blur = new EventEmitter<AddressInfo>();
|
||||
|
||||
ready = new EventEmitter<void>();
|
||||
|
||||
onChange = (value: AddressInfo) => { };
|
||||
onTouched = () => { };
|
||||
|
||||
constructor(
|
||||
private elementRef: ElementRef,
|
||||
private msgBoxService: MsgBoxService,
|
||||
private renderer: Renderer2,
|
||||
private cszService: CityStateZipService,
|
||||
) {
|
||||
|
||||
}
|
||||
ngOnInit() {
|
||||
}
|
||||
ngAfterViewInit() {
|
||||
this.renderer.removeAttribute(this.elementRef.nativeElement, 'id')
|
||||
this.ready.emit();
|
||||
}
|
||||
|
||||
ngAfterContentInit() {
|
||||
this._writing = false;
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.destroy$.next();
|
||||
this.destroy$.complete();
|
||||
}
|
||||
onBlur() {
|
||||
this.blur.emit(this.value);
|
||||
}
|
||||
|
||||
validate(control: AbstractControl): ValidationErrors {
|
||||
if (this.cszService.validateState(this.state)) {
|
||||
if (this.statePopover) {
|
||||
this.statePopover.invalid = false;
|
||||
this.statePopover.hide();
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (this.statePopover) {
|
||||
|
||||
this.statePopover.invalid = true;
|
||||
this.statePopover.invalidMsg = `${this.state} isn't a valid state or U.S. territory mail code!`;
|
||||
setTimeout(() => {
|
||||
this.statePopover.show();
|
||||
});
|
||||
}
|
||||
return { state: { message: 'Invalid state code.' } }
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
//#region Implements
|
||||
writeValue(value: AddressInfo): void {
|
||||
|
||||
if (value) {
|
||||
this.value = value;
|
||||
|
||||
//initial zip code with trimmed value
|
||||
this.value.zip = StringUtils.getTrimmedValue(this.value.zip);
|
||||
|
||||
this._oldValue = ObjectUtils.Clone(value);
|
||||
|
||||
const city = this.cszService.lookUpZipCode(this.city, this.state);
|
||||
if (city != null) {
|
||||
this.zipCodeList = city.zipCode.map((zipCode, i) => new DropDownOption(zipCode, zipCode));
|
||||
}
|
||||
}
|
||||
|
||||
this.onChange(this.value);
|
||||
}
|
||||
|
||||
registerOnChange(fn: (value: AddressInfo) => void): void {
|
||||
this.onChange = fn;
|
||||
}
|
||||
|
||||
registerOnTouched(fn: any): void {
|
||||
this.onTouched = fn;
|
||||
}
|
||||
|
||||
setDisabledState?(isDisabled: boolean): void {
|
||||
this.disabledState = isDisabled;
|
||||
}
|
||||
|
||||
zipCodeChanged(inputZip: string) {
|
||||
|
||||
if (!this._writing && !this.readonly &&
|
||||
StringUtils.getTrimmedValue(this._oldValue.zip) != StringUtils.getTrimmedValue(inputZip)
|
||||
) {
|
||||
|
||||
this._oldValue.city = null;
|
||||
this._writing = true;
|
||||
if (inputZip && inputZip.length < 10 && inputZip.length > 5) {
|
||||
|
||||
inputZip = inputZip.substring(0, 5);
|
||||
}
|
||||
this.value.zip = inputZip;
|
||||
|
||||
|
||||
const city = this.cszService.lookUpCity(this.zip);
|
||||
|
||||
if (city != null) {
|
||||
this.city = city.city;
|
||||
this.state = city.state;
|
||||
|
||||
this.zipCodeList = this.cszService
|
||||
.lookUpZipCode(this.city, this.state)
|
||||
.zipCode
|
||||
.map((zipCode, i) => new DropDownOption(zipCode, zipCode));
|
||||
this.zipcodeToCounty(this.zip);
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
this.onTouched();
|
||||
this._writing = false;
|
||||
}, 200);
|
||||
}
|
||||
}
|
||||
|
||||
cityOrStateChanged() {
|
||||
if (this.value.city) {
|
||||
this.value.city = this.value.city.replace(/^\s+|\s+$/g, '');
|
||||
}
|
||||
|
||||
if (!this._writing && !this.readonly) {
|
||||
if (this._oldValue.city != this.city || this._oldValue.state != this.state) {
|
||||
this._writing = true;
|
||||
|
||||
if (this.city && this.state && this.state.length == 2) {
|
||||
const city = this.updateZipCodesFromCity();
|
||||
if (city != null) {
|
||||
this.city = city.city;
|
||||
this.state = city.state;
|
||||
}
|
||||
else {
|
||||
if (false == this._msgShown) {
|
||||
this._msgShown = true;
|
||||
this.msgBoxService.show("Zip Code Not Found",
|
||||
{
|
||||
text: `Zip code for ${this.city}, ${this.state} not found.`,
|
||||
icon: ADIcon.WARNING
|
||||
})
|
||||
.pipe(first()).subscribe(result => {
|
||||
this._msgShown = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this._oldValue = ObjectUtils.Clone(this.value);
|
||||
setTimeout(() => {
|
||||
|
||||
this.onTouched();
|
||||
this._writing = false;
|
||||
}, 200);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
clearZipCode() {
|
||||
this.zipCodeList = [];
|
||||
this.zip = '';
|
||||
}
|
||||
|
||||
zipcodeToCounty(zip) {
|
||||
|
||||
if (zip) {
|
||||
this.cszService.getCounty(zip).subscribe((data) => {
|
||||
this.allData = data;
|
||||
|
||||
if (this.allData) {
|
||||
|
||||
let countyName = this.allData.County;
|
||||
countyName = countyName.toLowerCase().split(" ");
|
||||
for (let i = 0; i < countyName.length; i++) {
|
||||
countyName[i] = countyName[i][0].toUpperCase() + countyName[i].substr(1);
|
||||
}
|
||||
this.county = countyName.join(" ");
|
||||
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
// const countySearch = zipcodes.lookup(zip);
|
||||
// if (countySearch) this.county = countySearch.county;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private updateZipCodesFromCity(): CityInfo {
|
||||
this.state = this.state.toUpperCase();
|
||||
const city = this.cszService.lookUpZipCode(this.city, this.state);
|
||||
if (city != null) {
|
||||
this.zipCodeList = city.zipCode.map((zipCode, i) => new DropDownOption(zipCode, zipCode));
|
||||
} else {
|
||||
this.zipCodeList = [];
|
||||
}
|
||||
return city;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { CityStateZipComponent } from './city-state-zip.component';
|
||||
import { NbInputModule } from '@nebular/theme';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { MaskDirectiveModule } from '../../directives/mask-directive/mask-directive.module';
|
||||
import { DropDownListModule } from '../drop-down-list/drop-down-list.module';
|
||||
import { ForceFocusMsgModule } from '../../directives/force-focus-msg/force-focus-msg.module';
|
||||
import { RbjTooltipModule } from '../../directives/rbj-tooltip/rbj-tooltip.module';
|
||||
|
||||
@NgModule({
|
||||
declarations: [CityStateZipComponent],
|
||||
imports: [
|
||||
CommonModule,
|
||||
NbInputModule,
|
||||
FormsModule,
|
||||
MaskDirectiveModule,
|
||||
DropDownListModule,
|
||||
ForceFocusMsgModule,
|
||||
RbjTooltipModule,
|
||||
],
|
||||
exports: [CityStateZipComponent],
|
||||
})
|
||||
export class CityStateZipModule { }
|
||||
Reference in New Issue
Block a user