# Connecting UI Components to the Vuex Store | Full-Stack Google Contacts Clone with AdonisJS Framework (Node.js) and Quasar Framework (Vue.js)

**Author:** Ndianabasi Udonkang  
**Published:** 2021-11-06

In this lesson, we connect our UI components to the Vuex Store. The contacts data, contact edit form, and contact details page now points at the Vuex store.

---

## Tags

- [JavaScript](/llms/technical-blog/tag/javascript.md)
- [Nodejs](/llms/technical-blog/tag/nodejs.md)
- [Vuejs](/llms/technical-blog/tag/vuejs.md)
- [Adonisjs](/llms/technical-blog/tag/adonisjs.md)
- [Quasar Framework](/llms/technical-blog/tag/quasar-framework.md)

## Part of Series: [Full-Stack Google Contacts Clone with Node.js (Adonisjs Framework) and Vue.js (Quasar Framework)](/llms/technical-blog/series/clnwu9bc991hiu3wo2vwmw45/full-stack-google-contacts-clone-with-node-js-adonisjs-framework-and-vue-js-quasar-framework.md)

This article is part of the **[Full-Stack Google Contacts Clone with Node.js (Adonisjs Framework) and Vue.js (Quasar Framework)](/llms/technical-blog/series/clnwu9bc991hiu3wo2vwmw45/full-stack-google-contacts-clone-with-node-js-adonisjs-framework-and-vue-js-quasar-framework.md)** series.

### Articles in this Series:

- [Introduction to Full-Stack Google Contacts Clone with Node.js (Adonisjs Framework) and Vuejs (Quasar Framework)](/llms/technical-blog/article/xe5mlxg43s9rpx1pxsvfdp3r/introduction-to-full-stack-google-contacts-clone-with-node-js-adonisjs-framework-and-vuejs-quasar-framework.md)
- [Workspace Setup | Full-Stack Google Contacts Clone with Adonis.js/Node.js and Quasar (Vue.js)](/llms/technical-blog/article/fo8str8yjpkz5dygxg875bpw/workspace-setup-full-stack-google-contacts-clone-with-adonis-js-node-js-and-quasar-vue-js.md)
- [Frontend Overview | Full-Stack Google Contacts Clone with Adonis.js/Node.js and Quasar (Vue.js)](/llms/technical-blog/article/nxgmng6fhf1cwjk30coflro2/frontend-overview-full-stack-google-contacts-clone-with-adonis-js-node-js-and-quasar-vue-js.md)
- [The Left Drawer | Full-Stack Google Contacts Clone with Adonis.js/Node.js and Quasar (Vue.js)](/llms/technical-blog/article/m6fagvy2il5h65llxxbbl9os/the-left-drawer-full-stack-google-contacts-clone-with-adonis-js-node-js-and-quasar-vue-js.md)
- [Improve The Header | Full-Stack Google Contacts Clone with Adonis.js/Node.js and Quasar (Vue.js)](/llms/technical-blog/article/jjwbxcg450jmkkcrrmkmwvwj/improve-the-header-full-stack-google-contacts-clone-with-adonis-js-node-js-and-quasar-vue-js.md)
- [New Contact Form Design | Full-Stack Google Contacts Clone with Adonis.js/Node.js and Quasar (Vue.js)](/llms/technical-blog/article/ebguwpkntk3empakunrki4dq/new-contact-form-design-full-stack-google-contacts-clone-with-adonis-js-node-js-and-quasar-vue-js.md)
- [Validating the Contact Form with Vuelidate | Full-Stack Google Contacts Clone with Adonis.js/Node.js and Quasar (Vue.js)](/llms/technical-blog/article/z0xr9m2ljler2cwnpsw2ycwa/validating-the-contact-form-with-vuelidate-full-stack-google-contacts-clone-with-adonis-js-node-js-and-quasar-vue-js.md)
- [Designing the Contacts Table | Full-Stack Google Contacts Clone with Adonis.js/Node.js and Quasar (Vue.js)](/llms/technical-blog/article/h2p1x1z406ib38zu2dci3jur/designing-the-contacts-table-full-stack-google-contacts-clone-with-adonis-js-node-js-and-quasar-vue-js.md)
- [Designing the Contacts Table (Part 2) | Full-Stack Google Contacts Clone with Adonis.js/Node.js and Quasar (Vue.js)](/llms/technical-blog/article/j0iscua9gg8ekuz8ifh6chpn/designing-the-contacts-table-part-2-full-stack-google-contacts-clone-with-adonis-js-node-js-and-quasar-vue-js.md)
- [Refactoring the Main Layout | Full-Stack Google Contacts Clone with Adonis.js/Node.js and Quasar (Vue.js)](/llms/technical-blog/article/uktmlqsivwtr3cvnpld59k9q/refactoring-the-main-layout-full-stack-google-contacts-clone-with-adonis-js-node-js-and-quasar-vue-js.md)
- [Improving User Experience with the Contacts Table | Full-Stack Google Contacts Clone with Adonis.js/Node.js and Quasar (Vue.js)](/llms/technical-blog/article/dzs812auf61ej7qu2cg726dh/improving-user-experience-with-the-contacts-table-full-stack-google-contacts-clone-with-adonis-js-node-js-and-quasar-vue-js.md)
- [Designing the Contact Details Page | Full-Stack Google Contacts Clone with Adonis.js/Node.js and Quasar (Vue.js)](/llms/technical-blog/article/kf9pz97n63fvgfrcy5viwzrg/designing-the-contact-details-page-full-stack-google-contacts-clone-with-adonis-js-node-js-and-quasar-vue-js.md)
- [Creating the Contact Edit Page | Full-Stack Google Contacts Clone with Adonis.js/Node.js and Quasar (Vue.js)](/llms/technical-blog/article/fmr90prj84c9zn0633jc368g/creating-the-contact-edit-page-full-stack-google-contacts-clone-with-adonis-js-node-js-and-quasar-vue-js.md)
- [How Software Backends Work | Full-Stack Google Contacts Clone with AdonisJs (Node.js) and Quasar Framework (Vue.js)](/llms/technical-blog/article/aca8fcrghz70nn1wqg3uzbum/how-software-backends-work-full-stack-google-contacts-clone-with-adonis-js-node-js-and-quasar-framework-vue-js.md)
- [Setting Up The Backend | Full-Stack Google Contacts Clone with AdonisJs (Node.js) and Quasar Framework (Vue.js)](/llms/technical-blog/article/fxb49fmz9ljbwrimbnnzudfv/setting-up-the-backend-full-stack-google-contacts-clone-with-adonis-js-node-js-and-quasar-framework-vue-js.md)
- [Why Choose the AdonisJs Framework? | Full-Stack Google Contacts Clone with AdonisJs (Node.js) and Quasar Framework (Vue.js)](/llms/technical-blog/article/lnhm5oudx34yo2869gmcms37/why-choose-the-adonis-js-framework-full-stack-google-contacts-clone-with-adonis-js-node-js-and-quasar-framework-vue-js.md)
- [Setting Up Our API Server with AdonisJs Framework | Full-Stack Google Contacts Clone with AdonisJs (Node.js) and Quasar Framework (Vue.js)](/llms/technical-blog/article/x1jgmet84aqj9qggu5mulkh0/setting-up-our-api-server-with-adonis-js-framework-full-stack-google-contacts-clone-with-adonis-js-node-js-and-quasar-framework-vue-js.md)
- [Setting Up Postman with the API Server | Full-Stack Google Contacts Clone with AdonisJS (Node.js) and Quasar Framework (Vue.js)](/llms/technical-blog/article/v4qehksislosmuy92qgjr4ct/setting-up-postman-with-the-api-server-full-stack-google-contacts-clone-with-adonis-js-node-js-and-quasar-framework-vue-js.md)
- [The Model-View-Controller Design Pattern in AdonisJS | Full-Stack Google Contacts Clone with AdonisJS (Node.js) and Quasar Framework (Vue.js)](/llms/technical-blog/article/h5wqql65iejop5xrplujw458/the-model-view-controller-design-pattern-in-adonis-js-full-stack-google-contacts-clone-with-adonis-js-node-js-and-quasar-framework-vue-js.md)
- [Create Column Definitions & Insert New Contacts | Full-Stack Google Contacts Clone with AdonisJS (Node.js) and Quasar Framework (Vue.js)](/llms/technical-blog/article/asc2emsho4u73r6jb2gxjddw/create-column-definitions-and-insert-new-contacts-full-stack-google-contacts-clone-with-adonis-js-node-js-and-quasar-framework-vue-js.md)
- [Data Validation & Sanitisation with AdonisJS | Full-Stack Google Contacts Clone with AdonisJS (Node.js) and Quasar Framework (Vue.js)](/llms/technical-blog/article/bcey0ac7m5sbs9hqy1h824sl/data-validation-and-sanitisation-with-adonis-js-full-stack-google-contacts-clone-with-adonis-js-node-js-and-quasar-framework-vue-js.md)
- [Creating a Middleware and Updating a Contact | Full-Stack Google Contacts Clone with AdonisJS (Node.js) and Quasar Framework (Vue.js)](/llms/technical-blog/article/oqxzmtgfy1r3oewpnedc7wf6/creating-a-middleware-and-updating-a-contact-full-stack-google-contacts-clone-with-adonis-js-node-js-and-quasar-framework-vue-js.md)
- [Fetching and Deleting a Contact with AdonisJS | Full-Stack Google Contacts Clone with AdonisJS Framework (Node.js) and Quasar Framework (Vue.js)](/llms/technical-blog/article/p6bh0ponq43u3vi6o8uww84x/fetching-and-deleting-a-contact-with-adonis-js-full-stack-google-contacts-clone-with-adonis-js-framework-node-js-and-quasar-framework-vue-js.md)
- [Using Model Factories and Seeders in AdonisJS | Full-Stack Google Contacts Clone with AdonisJS Framework (Node.js) and Quasar Framework (Vue.js)](/llms/technical-blog/article/cfotveuxbgjtdp12l6pzcb2y/using-model-factories-and-seeders-in-adonis-js-full-stack-google-contacts-clone-with-adonis-js-framework-node-js-and-quasar-framework-vue-js.md)
- [Creating and Registering a Vuex Module | Full-Stack Google Contacts Clone with AdonisJS Framework (Node.js) and Quasar Framework (Vue.js)](/llms/technical-blog/article/cw96ewnzujgsz2e2garhbuzb/creating-and-registering-a-vuex-module-full-stack-google-contacts-clone-with-adonis-js-framework-node-js-and-quasar-framework-vue-js.md)
- **Connecting UI Components to the Vuex Store | Full-Stack Google Contacts Clone with AdonisJS Framework (Node.js) and Quasar Framework (Vue.js)** (Current Article)
- [Connecting the Frontend to the API Server | Full-Stack Google Contacts Clone with AdonisJS Framework (Node.js) and Quasar Framework (Vue.js)](/llms/technical-blog/article/fk4262359gg3g7f0jn95fn9b/connecting-the-frontend-to-the-api-server-full-stack-google-contacts-clone-with-adonis-js-framework-node-js-and-quasar-framework-vue-js.md)
- [Creating and Updating Contacts From the Frontend | Full-Stack Google Contacts Clone with AdonisJS Framework (Node.js) and Quasar Framework (Vue.js)](/llms/technical-blog/article/t15w2s3ao9jo74l96stjo7z6/creating-and-updating-contacts-from-the-frontend-full-stack-google-contacts-clone-with-adonis-js-framework-node-js-and-quasar-framework-vue-js.md)
- [Uploading Files and Creating Avatars for Contacts With AdonisJS and Axios](/llms/technical-blog/article/hg9ilaubqesm8angg0rhrjp9/uploading-files-and-creating-avatars-for-contacts-with-adonis-js-and-axios.md)
- [Deleting a Contact with AdonisJS and Vuejs](/llms/technical-blog/article/zdr4pxmgcax2dsfnwimhty0g/deleting-a-contact-with-adonis-js-and-vuejs.md)

---

## Article Content

In this lesson, we will connect our user interface components to the Vuex store. The components are the `ui/src/pages/Index.vue` which is the homepage component used for displaying the contacts table, `ui/src/pages/contacts/CreateContact.vue` used for creating and editing a contact, and `ui/src/pages/contacts/ViewContact.vue` used for displaying the details of a contact.

We will continue with our git branch: `17-connect-components-to-vuex-store`. If you aren't using that branch, do the following:

```bash
# Make sure you are within your project
git checkout 17-connect-components-to-vuex-store
```

## Pointing the `ui/src/pages/Index.vue` component at the Vuex Store

Open the `ui/src/pages/Index.vue` file. Refer to [this snapshot](https://github.com/ndianabasi/google-contacts/blob/18-connect-components-to-vuex-store/ui/src/pages/Index.vue) of the file. Copy-and-paste the content of the snapshot into the `ui/src/pages/Index.vue` file. The `template` section remains on changed. So we focus on the `script` section.

From Line 127 to 129, we disable some `eslint` rules for the entire `script` section. This is because the types of Vuex methods and modules are not compatible when they interact.

From Line 130 to 138, we import additional hooks and types from `vue`. At Line 139, we import the `Contact` interface from `ui/src/types.ts`.

At Line 141, we import the `useStore` hook/function from the Vuex store. The `useStore` hook will be used to instantiate the store inside our component.

At Line 147, we call `useStore()` to initialise our `store`.

At Line 148, we make the `loading` ref to be `true` by default. This ensures that when the contacts table is rendered, the loading indicator will be shown by default.

At Line 149, we set `pageSize` to `50`. This is the number of rows which will be fetch for each page of contacts requested from the API server.

At Line 150, we initialise the `nextPage` ref to `1`. The first page to be first is page 1. This value will be incremented after each page of contacts is fetched.

At Line 151, we define the `tableRef` which will be used to store the reference to the `QTable` component. This `tableRef` will be used later to call methods defined by `QTable`.

At Line 153, we replace the `rows` computed ref with the `contacts` computed ref. The `contacts` computed ref is used to automatically fetch and store the value of the `contacts/contactList` Vuex getter. The `contacts/contactList` getter contains all the contacts currently fetched from the API server for display on the contacts table.

```ts
    const contacts = computed(
      () => store.getters["contacts/contactList"] as Contact[]
    );
```

At Line 157, we define a computed ref `totalContacts` to store the value of the `contacts/totalContacts` getter.

```ts
    const totalContacts = computed(
      () => store.getters["contacts/totalContacts"] as number
    );
```

From Line 161 to 173, we employ the `watchEffect` hook so that the `contacts/LOAD_CONTACTS` action is dispatched immediately the component is created. Since the Vuex actions are promises, we have `await` them. The `contacts/contactList` action attaches a payload containing the `nextPage` and `pageSize` properties.

```ts
    const stopContactListEffect = watchEffect(async () => {
      await store
        .dispatch("contacts/LOAD_CONTACTS", {
          nextPage: nextPage.value,
          pageSize,
        })
        .then(() => {
          void nextTick(() => {
            tableRef.value?.refresh();
            loading.value = false;
          });
        });
    });
```

Also, the `watchEffect` hook is stored in the constant `stopContactListEffect`. At Line 230, we call the `onBeforeUnmount` hook with the `stopContactListEffect` as the handler. This removes the `watchEffect` from the component and prevent memory leaks. The `watchEffect` hook will automatically add variables used within it to its dependencies. If any of the values change, the `watchEffect` hook will re-run and lead to new contacts being fetched from the API server. By default, the `watchEffect` hook will run when the component is created in the `setup` hook.

When the `contacts/LOAD_CONTACTS` action is resolved, from Line 167, we call the `nextTick` hook and instruct the table to refresh (`tableRef.value?.refresh();`) and disable the loading state of the table (`loading.value = false;`). The `nextTick` hook is important so that the new value of the `contacts/contactList` getter is completely propagated and stored in the `contacts` computed ref at Line 153 before we refresh the table. Additionally, the `contacts/LOAD_CONTACTS` action will also update the `totalContacts` store property which will be propagated to the `totalContacts` computed ref at Line 157.

At Line 175, we define a constant `lastPage`, which stores the last page of all contacts we have based on the values of `pageSize` and `totalContacts` variables.

At Line 177, we modify the `onScroll` function which is attached to the `QTable` component as the handler of the `virtual-scroll` event (`@virtual-scroll="onScroll"`). See Line 28.

```diff
+    const onScroll = function ({ to, ref: ref2 }: VirtualScrollCtx): void {
      if (
        loading.value !== true &&
        nextPage.value < lastPage &&
-        to === lastIndex
+        to === nextPage.value * pageSize - 1
      ) {
+        tableRef.value = ref2;
        loading.value = true;
-
-        setTimeout(() => {
-          nextPage.value++;
-          void nextTick(() => {
-            ref2.refresh();
-            loading.value = false;
-          });
-        }, 500);
+        nextPage.value++;
      }
    };
```

The most important change above is the replacement of the `setTimeout` function (which was used to simulate an asynchronous operation) with `nextPage.value++`. When `nextPage.value` is incremented, it triggers the re-run of the `watchEffect` hook (because `nextPage` is a dependency in the `watchEffect` hook) at Line 161 which leads to the dispatch of the `contacts/LOAD_CONTACTS` action and population of the contacts table with new contacts for rendering.

At Line 217, we assign our `contacts` computed property to the `rows` property of the object returned from our `setup` hook.

At Line 243 and 244, we update the`pagination` property as shown below:

```diff
      pagination: {
        rowsPerPage: 0,
-        rowsNumber: rows.value.length,
+        page: nextPage.value,
+        rowsNumber: totalContacts.value,
      },
```

This concludes the changes in the `ui/src/pages/Index.vue` component.

## Pointing the `ui/src/pages/contacts/CreateContact.vue` component at the Vuex Store

Open the `ui/src/pages/contacts/CreateContact.vue` file. Refer to [this snapshot](https://github.com/ndianabasi/google-contacts/blob/18-connect-components-to-vuex-store/ui/src/pages/contacts/CreateContact.vue) of the file. Copy-and-paste the content of the snapshot into the `ui/src/pages/contacts/CreateContact.vue` file. The `template` section remains on changed. So we focus on the `script` section.

At Line 56, we disable the `no-misused-promises` eslint rule. At Line 64, we import the `nextTick` hook from `vue`. At Line 70, we import the `useStore` hook from our `store`.

Within the `setup` hook, at Line 94, the `useStore` hook is called and used to initialise the `store` constant.

At Line 274, we replace the `contact` reactive object with the `currentContact` computed property which stores the value of the `contacts/currentContact` getter from our Vuex store.

At Line 283, within the `watchEffect` hook, we introduce the `contacts/LOAD_CURRENT_CONTACT` action which is immediately dispatched when the component is created. The action sends the `contactId` prop as the `id` of the contact being viewed or edited. Remember that the contactId`prop is automatically set by`vue-router`when that route is visited. Then the action is resolved, we use the`nextTick\` hook to execute the iteration in the next process tick.

```ts
        await store
          .dispatch("contacts/LOAD_CURRENT_CONTACT", props.contactId)
          .then(() => {
            void nextTick(() => {
              Object.keys(currentContact.value).forEach((key) => {
                if (key !== "id") {
                  form[key].value = currentContact.value[key];
                }
              });
            });
          });
```

The iteration above (Lines 287 to 291) loops through the keys (omitting the `id` key) in the `currentContact` computed ref and assigns the value of the keys to the `value` property of each property in the `form` reactive object. This ensures that the fields on our form are populated with the values from the `currentContact` computed property.

This concludes the modifications in the `ui/src/pages/contacts/CreateContact.vue` component.

## Pointing the `ui/src/pages/contacts/ViewContact.vue` component at the Vuex Store

Open the `ui/src/pages/contacts/ViewContact.vue` file. Refer to [this snapshot](https://github.com/ndianabasi/google-contacts/blob/18-connect-components-to-vuex-store/ui/src/pages/contacts/ViewContact.vue) of the file. Copy-and-paste the content of the snapshot into the `ui/src/pages/contacts/ViewContact.vue` file. The `template` section remains on changed. So we focus on the `script` section.

From Lines 201 to 210, we define the `ContactData` type:

```ts
type ContactData = Array<{
  icon: string;
  text: string | undefined | null | Array<string | null | undefined>;
  label: string;
  key: string;
  side?: string | undefined;
  sideColor?: string | undefined;
  clampLines?: number | "none";
  linkAs?: "email" | "tel" | "website";
}>;
```

At Line 205, we remove the `reactive` hook from `vue` as it is no longer need. We also remove the `contacts` import from `Google_Contacts_Clone_Mock_Data`.

At Line 223, we import the `useStore` hook from our Vuex store.

At Line 235, we instantiate the `store` reference and the `currentContact` computed ref

```ts
    const store = useStore();
    const currentContact = computed(
      // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
      () => store.getters["contacts/currentContact"] as Contact
    );
```

At Line 242, within the `watchEffect` hook, we call the `contacts/LOAD_CURRENT_CONTACT` action with the `contactId` prop as the `id` payload to load the current contact for the route.

At Line 247, we update the `fullName` computed property to concatenate the `firstName` and `lastName` from the `currentContact` computed property. Same for the `jobDescription` computed property at Lines 251 to 253.

From Line 256, we update the text property of each `contactData` object to read from the `currentContact` computed property.

At Line 333 to 339, we return `currentCurrent`, `fullName`, `contactData`, `jobDescription`, and `isNullArray` from the `setup` hook so that they are all available in the `template` section.

Save all files, serve the frontend, and test the functionalities on the contact tables, and view and edit a contact.

```bash
cd ui
yarn serve
```

This concludes this lesson. In the next lesson, we will connect the frontend of the Google Contacts Clone app to the API server.

Commit, merge with the master branch, and push to the remote repository (GitHub).

```bash
git add .
git commit -m "feat(api): complete connection of components to vuex store"
git push origin 17-connect-components-to-vuex-store
git checkout master
git merge master 17-connect-components-to-vuex-store
git push origin master
```


*This document was generated from the live article page on https://ndianabasi.com/technical-blog/article/fdyqp17yehbnqrjvjvab4b5b/connecting-ui-components-to-the-vuex-store-full-stack-google-contacts-clone-with-adonis-js-framework-node-js-and-quasar-framework-vue-js • 2026-06-07*
