import { AddressClient } from '../../clients/AddressClient'; import { AddressUtils } from '../../util/AddressUtils'; import { Address } from '../../entities/Address'; export const EnterButtonKeyCode = 13; export function AddressPickerDirectiveFactory() { return new AddressPickerDirective(); } class AddressPickerDirective implements ng.IDirective { restrict = 'E'; bindToController = true; controller = AddressPickerController; controllerAs = 'ctrl'; require = "^form"; template = require('./AddressPicker.html'); scope = { providedPostcode: '=forPostcode', initialValue: '=', disableValidation: '=', onChange: '&', name: '@', label: '@' } link(scope, element: JQuery, attributes: ng.IAttributes, controller: ng.IController) { scope.form = controller; } } // Remove this when we don't need to use angular DI for injecting this class const addressUtils = new AddressUtils(); export class AddressPickerController { // Input postcode to look up addresses with - if not provided as an attribute, then we display a postcode field in this component providedPostcode?: string; // Address to initialise the dropdown with (e.g. from local storage). Bound to the parent scope using the component attribute above initialValue?: Address; // disable validation disableValidation?: boolean; // Callback to trigger when the selector or manual address form is changed onChange: ({ address: Address }) => void; // Postcode from the displayed lookup field - i.e. when providedPostcode is absent, and we are not using a manual address form postcode: string; // Actual address that is selected, or filled in manually address: Address; // List of addresses that come back from searching by postcode (in the API) addresses: Address[]; // Is the API request still in progress? private loading: boolean; // Has the user indicated that their address is not in the list? i.e. they want to fill in a manual address manualAddressVisible: boolean; // In the same way the main form uses form.$submitted for displaying validation messages, we only display the postcode error if // the Find button has been clicked clickedFind: boolean; constructor(private addressClient: AddressClient) { this.manualAddressVisible = false; this.clickedFind = false; this.address = this.initialValue; // If the postcode is provided, load the list of addresses immediately if (this.providedPostcode) { this.loadAddresses(this.providedPostcode); this.postcode = this.providedPostcode; } // If the postcode is not provided but the initial value contains an address with a postcode else if (this.initialValue && this.initialValue.postcode) { this.postcode = this.initialValue.postcode; this.loadAddresses(this.postcode); } } loadAddresses(postcode: string) { // When the postcode is provided, this is meaningless; but it makes sense to set now, in the case where postcode is not provided if (!this.disableValidation) { this.clickedFind = true; // If the postcode doesn't validate then angular doesn't bind the value to the model, so this field will be undefined if (postcode) { this.loading = true; this.addressClient.getAddresses(postcode).then( addresses => { this.addresses = addresses.data.map(address => ({ ...address, manualAddress: false })); this.loading = false; }, () => { this.loading = false; } ); } } } postcodeKeypress(event: KeyboardEvent) { // Detect an Enter keypress (necessary because it's not a submit button) if (event.keyCode === EnterButtonKeyCode) { event.preventDefault(); this.loadAddresses(this.postcode); } }; showManualAddress() { this.manualAddressVisible = true; this.address = { addressLine1: '', addressLine2: '', town: '', // Again this will only work if the postcode was provided, or if the postcode lookup was filled in with a valid value postcode: this.providedPostcode || this.postcode, manualAddress: true }; } updateManualAddress() { this.onChange({ address: addressUtils.addFullAddress(this.address) }); } updatePostcode() { this.onChange({address: {postcode: this.postcode}}); this.loadAddresses(this.postcode); } }