This package provides a UI implementation which connects with the MyParcel Address API. This provides a way to easily integrate the address validation and autocomplete functionality into your webshop. The Address Widget is, or will be, included in our plugins and PDK. Please refer to this readme on how to include it in your own project if you don't use one of those.
[!NOTE] You need an active MyParcel account and API key to be able to use this package.
The following instructions will help you to include the Address Widget in your project. Note there are various ways to include the widget, depending on your needs and not all of them are covered here.
You can include the widget via a script tag. This is the easiest way to include the widget in your project.
<body>
<div id="myparcel-address-widget"></div>
<script src="https://cdn.jsdelivr.net/npm/@myparcel/address-widget"></script>
</body>You can also include the widget in your project by installing it via a package manager. This is the recommended way to include the widget in your project and gives you the most control.
pnpm add @myparcel/address-widgetyarn add @myparcel/address-widgetEven though you can initialize the config using the methods below, you will probably need to update some information in the widget after it has been initialized, or react to information from it.
You can dispatch the CONFIGURATION_UPDATE_EVENT from your code whenever something in your application changes. This will update the widget with the new configuration.
If you provide a partial config object, only the properties that are present in the object will be updated. To specifically unset a property, you can set it to null.
import {CONFIGURATION_UPDATE_EVENT} from '@myparcel/address-widget';
const myConfig = {
apiUrl: 'https://my-webshop.nl/proxy-api',
};
changeCountry = (countryCode) => {
const event = new CustomEvent(CONFIGURATION_UPDATE_EVENT, {
detail: {
config: {
address: {countryCode},
},
},
});
document.dispatchEvent(event);
};You can listen for validation errors triggered by the address widget using the MYPARCEL_VALIDATION_ERROR_EVENT. This event is dispatched when the user input fails validation (e.g., incomplete or incorrect address fields).
import {MYPARCEL_VALIDATION_ERROR_EVENT} from '@myparcel/address-widget';
window.addEventListener(MYPARCEL_VALIDATION_ERROR_EVENT, (event) => {
console.warn('Address validation error:', event.detail);
// You can use event.detail to access validation error information
});This is useful if you want to give custom feedback to users or log validation issues.
In order to be able to add multiple instances of the widget on one page, you may optionally provide an appIdentifier to your config to only update that instance of it.
import {CONFIGURATION_UPDATE_EVENT} from '@myparcel/address-widget';
const myConfig = {
apiUrl: 'https://my-webshop.nl/proxy-api',
appIdentifier: 'my-app-identifier',
};
changeCountry = (countryCode) => {
const event = new CustomEvent(CONFIGURATION_UPDATE_EVENT, {
detail: {
appIdentifier: myConfig.appIdentifier,
config: {
address: {countryCode},
},
},
});
document.dispatchEvent(event);
};If you consume the widget via a script tag, you can pass the configuration via the window object. You currently cannot choose a country within the widget itself, you need to pass it as a configuration option. When the country is not set, the widget will default to the Netherlands. Other countries are not supported at this time and setting them will hide the widget.
Doing this, however, will always expose your API key to the client. You should use an API proxy to keep your key secret.
Example:
<script>
window.MyParcelAddressConfig = {
apiKey: 'MY_API_KEY',
address: {countryCode: 'NL'},
};
</script>[!DANGER] This will expose your API key to anyone visiting your website. You should only use this method for local testing purposes. You should use a proxy server to keep your API key secret.
Example using a proxy server:
<script>
window.MyParcelAddressConfig = {
apiUrl: 'https://your-api-proxy.com',
address: {countryCode: 'NL'},
};
</script>If you include the widget in your project using a package manager, you can configure the widget by passing the configuration object to the TheAddressWidget component. The configuration object should contain the following properties:
<script>
import {TheAddressWidget} from '@myparcel/address-widget';
</script>
<template>
<TheAddressWidget
:config="{apiKey: 'MY_API_KEY', address: {countryCode: 'NL'}}" />
</template>[!WARNING] While this method is more secure than using the
windowobject, it is still not recommended to use this method in a production environment. You should use a proxy server to keep your API key secret.
Example using a proxy:
<script>
import {TheAddressWidget} from '@myparcel/address-widget';
</script>
<template>
<TheAddressWidget
:config="{
apiUrl: 'https://my-webshop.com/proxy-api',
address: {countryCode: 'NL'},
}" />
</template>The widget is a Vue component and can be used in both Vue and non-Vue projects. It is intended to be injected in an existing form, where you would need to add a collection point for the data emitted by this widget.
The most important event emitted is adress-selected, which contains the validated address data.
Some examples on how to listen to these events and use the emitted data:
<template>
<h2>My checkout form</h2>
<form @submit.prevent="doSubmit">
<input
type="text"
name="name"
placeholder="Name" />
<input
type="text"
name="phone"
placeholder="Phone number" />
<h3>Shipping address</h3>
<TheAddressWidget @address-selected="setShippingAddress" />
<h3>Billing address</h3>
<input
type="checkbox"
name="syncShipmentAndBilling"
id="syncShipmentAndBilling"
v-model="syncShipmentAndBilling" />
<label for="syncShipmentAndBilling">Use the same address for billing</label>
<TheAddressWidget
@address-selected="setBillingAddress"
v-if="!syncShipmentAndBilling" />
<button type="submit">Submit</button>
</form>
</template>
<script setup>
import {ref} from 'vue';
import {TheAddressWidget} from '@myparcel/address-widget';
import {type Address} from '@myparcel/address-widget';
const syncShipmentAndBilling = ref(false);
const billingAddress<Address> = ref(null);
const shippingAddress<Address> = ref(null);
const setShippingAddress = (data: Address) => {
shippingAddress.value = data;
if (syncShipmentAndBilling.value) {
setBillingAddress(data);
}
};
const setBillingAddress = (data: Address) => {
setBillingAddress.value = data;
};
const = doSubmit = () => {
// Reject if the addresses are not filled in
if (!shippingAddress.value || !billingAddress.value) {
return;
}
// Do something with the addresses
fetch('/my-api-implementation/checkout', {
method: 'POST',
body: JSON.stringify({
shippingAddress: shippingAddress.value,
billingAddress: billingAddress.value,
}),
});
};
</script><html lang="en">
<body>
<h1>My checkout form</h1>
<form
target="/my-php-backend/checkout"
method="POST">
<input
type="text"
name="name"
placeholder="Name" />
<input
type="text"
name="phone"
placeholder="Phone number" />
<h2>Shipping address</h2>
<input
type="hidden"
name="shippingAddress"
id="shippingAddress" />
<!-- The widget will be displayed here, see javascript below! -->
<div id="shipping-address-widget"></div>
<h2>Billing address</h2>
<input
type="checkbox"
name="syncShipmentAndBilling"
id="syncShipmentAndBilling" />
<label for="syncShipmentAndBilling">
Use the same address for billing
</label>
<input
type="hidden"
name="billingAddress"
id="billingAddress" />
<!-- The widget will be displayed here, see javascript below! -->
<div id="billing-address-widget"></div>
<button type="submit">Submit</button>
</form>
<!-- Adding vue is required -->
<script src="https://cdn.jsdelivr.net/npm/vue@3"></script>
<script src="https://cdn.jsdelivr.net/npm/@myparcel/address-widget"></script>
<script src="./my-script.js"></script>
</body>
</html>import {
default as TheAddressWidget,
ADDRESS_SELECTED_EVENT,
} from '@myparcel/address-widget';
const SHIPPING_ID = 'shipping-address-widget';
const BILLING_ID = 'billing-address-widget';
// Define a reusable factory function so we can have multiple apps
const app = () => Vue.createApp(TheAddressWidget);
// Mount on shipping
app().mount(SHIPPING_ID);
// Mount on billing
app().mount(BILLING_ID);
// Listen for changes to the address
document.addEventListener(ADDRESS_SELECTED_EVENT, (event) => {
const address = event.detail;
// Check which widget was changed
if (event.target.id === SHIPPING_ID) {
// Load the address into a a hidden input. This assumes some backend script, like PHP, will handle the data when the form is submitted.
document.getElementById('shippingAddress').value = JSON.stringify(address);
if (document.getElementById('syncShipmentAndBilling').checked) {
document.getElementById('billingAddress').value = JSON.stringify(address);
}
} else if (event.target.id === BILLING_ID) {
document.getElementById('billingAddress').value = JSON.stringify(address);
}
});To get started, we recommend using nvm for Node management.
Before running node commands, you should run nvm use to switch to the correct
node version.
This repository uses pnpm as a package manager. The easiest way
to use / install it, is by enabling corepack: corepack enable, after running nvm use.
nvm usecorepack enablepnpm i
- Run
pnpm ito install dependencies. - Run
pnpm run translations:importto download the translations. - The project is now ready to use.
- Run
pnpm devto start the development environment - Run
pnpm testto run the tests - Run
pnpm previewto run the project in a production-like environment (also used for the demo environment)
pnpm ipnpm run devpnpm run previewpnpm run buildRun Unit Tests with Vitest
pnpm run test:unitLint with ESLint
pnpm run lintWe suggest you use an editor with ESLint and Prettier support. This will help
you keep the code clean and consistent. Commits should follow the Conventional
Commits specification. You might want to install the commitizen-package to
help you with this.
Please read our contribution guidelines.