Skip to main content

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 and destroyWidget functions.
  • 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 MobX reaction function also returns a disposer function, which can be called to stop the reaction.
    We store all disposers in the disposers 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 to extendConfig, not one wrapped in a Proxy.

    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.