Vue.js integration
This guide is intended for developers looking to integrate a Tamaro widget into a Vue.js application.
To get started, you can check out a sample implementation here:
https://stackblitz.com/edit/tamaro-vue-integration-example.
The guide will cover the following topics:
- How to instantiate Tamaro:
- Describes the right way to create a valid Tamaro instance.
- Dynamic Tamaro script loading:
- Defines separate
showWidget
,hideWidget
anddestroyWidget
functions.
- Defines separate
- Dynamic Tamaro instance initialization:
- Dynamically loads Tamaro script, initializes Tamaro instance and renders it.
- Destroying Tamaro instance:
- Destroys Tamaro instance on the wrapper component unmount.
- Cleans up subscriptions, effects and other related app state.
- State synchronization:
- From Tamaro instance state to Vue.js app state.
- From Vue.js app state to Tamaro instance state.
How to instantiate Tamaro
Avoid wrapping the Tamaro instance in Vue.js reactive state variables,
such as using ref()
function.
Since a Tamaro instance is a complex object that internally uses Proxy
objects,
wrapping it in Vue.js reactive state variables could cause it to break.
When the Tamaro instance is created using the rnw.tamaro.runWidget(...)
function,
or through the separate rnw.tamaro.createWidget(...)
and rnw.tamaro.renderWidget(...)
functions,
it is exposed to the window object as window.rnw.tamaro.instance
and can be accessed directly.
Therefore, there is no need to wrap it or store it in a Vue.js state.
Dynamic script loading and widget initialization
Add the container element to the template with id rnw-tamaro-widget
.
The Tamaro instance will be rendered there.
Next, define the showWidget
function like the following. When called, it will:
- Load Tamaro script and initializes the widget instance (if Tamaro script is not loaded yet).
- Render the Tamaro widget instance.
import {ref} from 'vue';
// ...
const tamaroConfig = ref({
paymentMethods: ['card', 'paypal'],
amounts: [1, 2, 3, 4],
// ... other tamaro config options
});
const showWidget = async () => {
try {
if (!window.rnw?.tamaro) {
await loadScript(
'https://tamaro.raisenow.com/raisenow-docs/latest/widget.js',
);
setupEvents(); // <-- configure events handling in a separate "setupEvents" function (see below)
await window.rnw.tamaro.createWidget(tamaroConfig.value);
}
await window.rnw.tamaro.renderWidget(
window.rnw.tamaro.instance,
'#rnw-tamaro-widget',
);
} catch (error) {
// handle error
}
};
If you want the Tamaro widget to be displayed immediately when the wrapper component is mounted, you can call
showWidget
within the onMounted
lifecycle hook:
import {onMounted} from 'vue';
// ...
onMounted(() => {
showWidget();
});
The loadScript
function is a simple helper function, like the following:
const loadScript = (src) => {
const script = document.createElement('script');
script.src = src;
script.async = true;
document.head.appendChild(script);
return new Promise((resolve, reject) => {
script.onload = () => {
resolve();
};
script.onerror = (error) => {
reject(error);
};
});
};
Destroying a Tamaro instance
A Tamaro instance can be destroyed like this:
import {onUnmounted} from 'vue';
// ...
const destroyWidget = () => {
// clean up all subscriptions and effects here
window.rnw?.tamaro?.destroy();
// clean up all other relevant app state here
};
onUnmounted(() => {
destroyWidget();
});
State synchronization
Avoid wrapping parts of the Tamaro state with wrappers like ref()
.
Instead, define your own reactive state and update it as needed when the Tamaro state changes.
You can also update the Tamaro state when your own reactive state changes.
For instance, if you define Tamaro configuration options in your app,
you might want to update the Tamaro instance accordingly.
The setupEvents
function enables you to define event handlers, reactions,
and watchers to synchronize the state between the Tamaro instance and the Vue.js app.
It should be called once after the Tamaro script is loaded,
but before the Tamaro instance is created (see the example in the showWidget
function above).
Defining the app state
A sample app state can be defined as follows:
import {ref} from 'vue';
// ...
const disposers = ref([]);
const tamaroConfig = ref({
paymentMethods: ['card', 'paypal'],
amounts: [1, 2, 3, 4],
// ... other tamaro config options
});
const formData = ref({});
const validationErrors = ref({});
const requiredFields = ref({});
Updating a Vue.js state from a Tamaro instance state
The variables formData
, validationErrors
, and requiredFields
mentioned above
are examples of reactive state variables.
They will be updated whenever the corresponding parts of the Tamaro instance state change.
You may use other Tamaro instance state properties as well.
Tamaro event handlers can be defined in the following way to synchronize the state from the Tamaro instance with the Vue.js app:
const setupEvents = () => {
// ...
disposers.value.push(
window.rnw?.tamaro.events.afterCreate.subscribe(async (event) => {
const tamaro = event.data.api;
const {reaction, comparer, toJS} = await window.rnw.tamaro.libs.mobx();
disposers.value.push(
reaction(
() => toJS(tamaro.paymentForm.data),
(data) => {
console.log('--> effect tamaro.paymentForm.data changed', data);
formData.value = data;
},
{
fireImmediately: true,
equals: comparer.structural,
},
),
);
disposers.value.push(
reaction(
() => toJS(tamaro.paymentForm.allValidationErrors),
(errors) => {
console.log(
'--> effect tamaro.paymentForm.allValidationErrors changed',
errors,
);
validationErrors.value = errors;
},
{
fireImmediately: true,
equals: comparer.structural,
},
),
);
disposers.value.push(
reaction(
() => toJS(tamaro.paymentForm.requiredFields),
(fields) => {
console.log(
'--> effect tamaro.paymentForm.requiredFields changed',
fields,
);
requiredFields.value = fields;
},
{
fireImmediately: true,
equals: comparer.structural,
},
),
);
// ...
}),
);
};
In the example above:
-
Tamaro uses the MobX library internally to manage its state. MobX utilities are accessible through the
window.rnw.tamaro.libs.mobx()
function, which enables setting up reactive effects for Tamaro state changes. For more information on MobX reactions, visit: https://mobx.js.org/reactions.html#reaction. -
Tamaro's
subscribe
function returns a disposer function, which can be called to unsubscribe from the event. Similarly, the MobXreaction
function also returns a disposer function, which can be called to stop the reaction.
We store all disposers in thedisposers
array state variable, allowing us to call them all when destroying the Tamaro instance to clean up all subscriptions and effects.const destroyWidget = () => {
disposers.value.forEach((dispose) => dispose?.());
disposers.value = [];
window.rnw?.tamaro?.destroy();
// clean up all other relevant app state here
};
Updating a Tamaro instance state from a Vue.js state
The tamaroConfig
reactive state variable is used to define the configuration options for the Tamaro widget.
You'll likely want the Tamaro widget to update immediately when the tamaroConfig
state variable changes.
You can handle this in the following way:
import {watch} from 'vue';
// ...
const setupEvents = () => {
// ...
disposers.value.push(
window.rnw?.tamaro.events.afterCreate.subscribe(async (event) => {
const tamaro = event.data.api;
// ...
disposers.value.push(
watch(
tamaroConfig,
(proxyObj) => {
const config = unwrapProxy(proxyObj);
console.log('--> APP tamaroConfig changed');
tamaro.extendConfig(config);
},
{deep: true, immediate: false},
),
);
}),
);
};
In the example above:
-
The watch function is a Vue.js function that monitors changes in the tamaroConfig state variable. When it detects a change, it triggers the callback function, which updates the Tamaro instance configuration with the new options. It returns the dispose function, which can be called to stop watching for changes.
-
The
unwrapProxy
function is a helper that takes a Proxy object as an argument and returns an unwrapped plain object. It's crucial to pass a plain object toextendConfig
, not one wrapped in aProxy
.import {cloneDeep} from 'lodash';
// ...
export const unwrapProxy = (proxyObj) => {
return cloneDeep(proxyObj);
};
Checking in the browser console
For demonstration purposes, the tamaroConfig
variable is exposed on the window
object:
import {ref} from 'vue';
// ...
const tamaroConfig = ref({
// ... tamaro config options
});
window.tamaroConfig = tamaroConfig;
To quickly see how changes to the tamaroConfig
variable are reflected in the Tamaro instance,
refer to the example:
-
Go to the StackBlitz example: https://stackblitz.com/edit/tamaro-vue-integration-example
-
Open preview in a new browser tab.
-
Open browser console.
-
Update
tamaroConfig
variable in the browser console:window.tamaroConfig.value = {
amounts: [5, 6, 7, 8],
}; -
You should see that Tamaro widget is updated accordingly right away.