# Designing the Contact Details Page | Full-Stack Google Contacts Clone with Adonis.js/Node.js and Quasar (Vue.js)

**Author:** Ndianabasi Udonkang  
**Published:** 2021-09-22

In this lesson, we will design the contact details page for viewing the details/properties of each contact. We will also create the route for the page.

---

## Tags

- [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)** (Current Article)
- [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)](/llms/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.md)
- [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 design the contact details page for viewing the details/properties of each contact. We will begin by adding a new route to our application and then adding an `click` event listener to the rows on our Contacts page so that when users click on any row/contact, the details of that contact will be opened.

Let's start by creating a new branch for our repo:

```bash
# Make sure you are within your project
git checkout -b 08-designing-the-contact-detail-page
```

The video below shows what we will achieve in this lesson:

%\[https://vimeo.com/611475107]

### Setup

Let's make some few changes to other files before we design the Contact Details page.

#### 1. Improve `ui/.eslintrc.js`

Open `ui/.eslintrc.js` and add the line below. Refer to this [snapshot](https://github.com/ndianabasi/google-contacts/blob/08-designing-the-contact-detail-page/ui/.eslintrc.js).

```diff
    "@typescript-eslint/explicit-module-boundary-types": "off",
+    "@typescript-eslint/restrict-template-expressions": "off",

    // allow debugger during development only
    "no-debugger": process.env.NODE_ENV === "production" ? "error" : "off",
```

#### 2. Improve `ui/src/router/routes.ts` and Add New Route

Open `ui/src/router/routes.ts` and make the following changes. Refer to [this snapshot](https://github.com/ndianabasi/google-contacts/blob/08-designing-the-contact-detail-page/ui/src/router/routes.ts).

```diff
    children: [
      {
        path: "",
        name: "home",
        component: () => import("pages/Index.vue"),
-        meta: { title: "Home" },
+        meta: { title: "Home", showDefaultTitle: false },
      },
      {
        path: "contacts/new",
        name: "new_contact",
        component: () => import("pages/contacts/CreateContact.vue"),
-        meta: { title: "New Contact" },
+        meta: { title: "New Contact", showDefaultTitle: true },
+      },
+      {
+        path: "contacts/:contactId/details",
+        name: "view_contact",
+        component: () => import("pages/contacts/ViewContact.vue"),
+        meta: { title: "View Contact", showDefaultTitle: false },
+        props: true,
      },
    ],

```

Here, we are perform two tasks:

1. For the existing routes: `home` and `new_contact`, we add an extra properties to the `meta` property: `showDefaultTitle`. This property will be used to determine if the `title toolbar` above that page will be displayed or not. So, we set `showDefaultTitle` to `false` and `true` for the `home` and `new_contact` routes, respectively.

2. We add a new route definition for our Contact Details page. The route is named `view_contact` and points at `"pages/contacts/ViewContact.vue"` as the route component. `showDefaultTitle` is set to `false`. Most importantly, we introduce a new route property called `props`. The `props` property is a standard `Vue Router` route property used to make all `route parameters` defined in the route `path` to be available in the route `component` as props. In this case, we have the path: `"/contacts/:contactId/details"`. This path contains one route parameter: `contactId`. A route parameter is introduced into a route by appending it with a colon (`:`). `Vue Router` is match the path and extract the route paramaters (if more than one) from the path. When `props` is set to `true`, these route parameters will be made available within your route component (in this case, `pages/contacts/ViewContact.vue`) as part of the component props. You do not have to define these props set from `Vue Router` within the component. But defining them is a good practice for readability and type-checking.

   #### Let's dive into route paths

So, for a URL like this: `http://localhost:8008/#/contacts/309b20ac-bbcd-4268-b01f-43770527540d/details`. Our actual route path is: `/contacts/309b20ac-bbcd-4268-b01f-43770527540d/details`. Within the route path for the `view_contact` route, we didn't add the leading (beginning) forward slash (`/`) because it was already defined as the path of the `layout` (`layouts/MainLayout.vue`) route. Since our `view_contact` route is a child of the `layout` route, `Vue Router` will append the leading `/`. Now, `Vue Router` will match the actual path and the route path. For example: `contacts/309b20ac-bbcd-4268-b01f-43770527540d/details` will be matched with `contacts/:contactId/details`. The route parameter `contactId` will be set to `309b20ac-bbcd-4268-b01f-43770527540d`. Within `pages/contacts/ViewContact.vue`, the prop `contactId` will be programmatically assigned the value `309b20ac-bbcd-4268-b01f-43770527540d`. We will make use of this prop and its value when we want to fetch the properties of the current contact being value within `pages/contacts/ViewContact.vue`. This will be discussed soon.

#### 3. Improve `ui/src/layouts/MainLayout.vue`

Open `ui/src/layouts/MainLayout.vue` and change Line 224 as below. Here, we are making use of the `meta.showDefaultTitle` property of our `route` definition as the condition for displaying the default `title toolbar` above our pages. In this case, the `title toolbar` won't be displayed since the `home` route has the property: `meta.showDefaultTitle` equals `false`. Refer to [this snapshot](https://github.com/ndianabasi/google-contacts/blob/08-designing-the-contact-detail-page/ui/src/layouts/MainLayout.vue).

```diff
-        v-if="$route.name !== 'home'"
+        v-if="$route.meta.showDefaultTitle"
```

#### 4. Add the `click` Event Listener to `Contacts` rows

Open `ui/src/pages/Index.vue` and add the following lines from Line 57. Refer to [this snapshot](https://github.com/ndianabasi/google-contacts/blob/08-designing-the-contact-detail-page/ui/src/pages/Index.vue). Here we are adding the `click` event listener to each table row (`q-tr`). We assign the `Vue Router`'s `push` method as the event handler so that we can navigate to the `view_contact` route (i.e. the Contact Details page) for that specific row (`props.row.id`).

```diff
        <template #body="props">
          <q-tr
            :props="props"
            @mouseover="handleMouseEvents"
            @mouseleave="handleMouseEvents"
+            @click.stop.prevent="
+              $router.push({
+                name: 'view_contact',
+                params: { contactId: props.row.id },
+              })
+            "
          >
```

You will also notice that we assigned an object: `{ contactId: props.row.id }` as the value of the `params` property of the `$router.push`'s payload. The `params` object is used to specify all the parameters (and their values) of the route we want to navigate to. For our `view_contact` route, we have one parameter (`contactId`), so we assign `props.row.id` as the value of the `contactId` parameter. If the route we are navigating to does not have any route parameter, there is no need assigning the `params` property at all.

### Contact Details Page Design

Refer to [this snapshot](https://github.com/ndianabasi/google-contacts/blob/08-designing-the-contact-detail-page/ui/src/pages/contacts/ViewContact.vue) as the content of our Contact details page.

Create and open the file: `ui/src/pages/contacts/ViewContact.vue`

```bash
# Ensure that you are in the root directory of your application
code ui/src/pages/contacts/ViewContact.vue 
# Opens the `ViewContact.vue` file with VS code
# CTRL+S to save for the first time
```

Copy the entire content of [the snapshot](https://github.com/ndianabasi/google-contacts/blob/08-designing-the-contact-detail-page/ui/src/pages/contacts/ViewContact.vue) into `ui/src/pages/contacts/ViewContact.vue`.

Now, let's discuss what's going on within the file. Beginning from the `script` section.

#### The `script` section

From Line 202, we define our props and assign `contactId` as our only prop for this component. This prop will be automatically injected by `Vue router` as discussed in [this section](#lets-dive-into-route-paths). However, we still define it for the purpose of readability and type-checking by TypeScript. If we do not explicitly define it, TypeScript will be unable of the `props.contactId` object within our `setup` function. We use the imported `PropType` type from `vue` to properly cast the `type` of the prop to TypeScript's `string` type. Read more about [type annotations for props](https://v3.vuejs.org/guide/typescript-support.html#annotating-props).

```ts
  props: {
    contactId: {
      type: String as PropType<string>,
      required: true,
      default: "",
    },
  },
```

At Line 209, we declare and initialise our `contact` variable. The `contact` variable will hold the contact to be displayed on the page. We assign properties to the object because of TypeScript type-checking.

```ts
let contact: Contact | null = reactive({
      id: "",
      firstName: "",
      surname: "",
      email1: "",
      phoneNumber1: "",
    });
```

At Line 217, we call the `watchEffect` hook (imported from `vue`). The `watchEffect` hook is used to execute statements when the `setup` function is called during the rendering of the application. Read more about Vue's `watchEffect` [here](https://v3.vuejs.org/guide/reactivity-computed-watchers.html#watcheffect). The `watchEffect` hook takes a handler function as the first parameter. Within the handler, we filter the `contacts` array imported from `/ui/src/data/Google_Contacts_Clone_Mock_Data` to obtain the current contact being viewed. We make use of the prop (`contactId`) which is automatically set by `Vue Router` as discussed earlier in [this section](#lets-dive-into-route-paths). We return the result of the filtering operation into `fetchedContact`. `fetchedContact` is an array. On Line 221, we destructure `fetchedContactObject` from the `fetchedContact` array. `fetchedContactObject` contains the object for our contact. We assign that object to our `contact` variable.

```ts
    const stopContactsEffect = watchEffect(() => {
      const fetchedContact = contacts.filter(
        (cont) => cont.id === props.contactId
      );
      const [fetchedContactObject] = fetchedContact;
      contact = fetchedContactObject;
    });
```

The `watchEffect` hook returns a function which is store in the `stopContactsEffect` constant. On Line 319, we call that function to stop the `watchEffect` hook before the component unmounts.

```ts
    onBeforeUnmount(() => {
      void stopContactsEffect();
    });
```

> Though [Vue will automatically stop](https://v3.vuejs.org/guide/reactivity-computed-watchers.html#stopping-the-watcher) the `watchEffect` for you, I demonstrated it here because it best practice to explicitly stop your `watchEffect`s to prevent memory leaks in your application.

At Line 225, we compute our contact's full name from the `firstName` and `lastName` properties.

```ts
    const fullName = computed(
      () => `${contact?.firstName} ${contact?.surname}`
    );
```

At Line 229, we compute the job description of the contact from the `jobTitle` and `company` properties. We use of the `String.trim()` method ensures that any trailing or leading spaces are removed.

```ts
    const jobDescription = computed(() =>
      `${contact?.jobTitle ?? ""}${contact?.jobTitle ? " at " : ""}${
        contact?.company ?? ""
      }`.trim()
    );
```

At Line 235, we compute the `contactData` by returning an array containing new properties which will be used for rendering the contact on the page. The array is should contain the following properties:

```ts
{
        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";
}
```

It is important to note that the `text` property can be a `string` or an array of `string`s. The array of `string`s is used when specifying the  object for `address`. Within the `template` section, you will see how we check for this array and modify how the labels are rendered with an array is detected.

At Line 323, we return `contact`, `fullName`, `contactData`, `jobDescription`, and `isNullArray` to the template section.

```ts
return { contact, fullName, contactData, jobDescription, isNullArray };
```

#### The `template` section

The Contact view page is designed to be responsive. Save the `ui/src/pages/contacts/ViewContact.vue` file. Take some time to study the `template` section.

Ensure that your dev server is running:

```bash
# Ensure that you are in root directory
cd ui
yarn serve
# Allow frontend to be compiled and served
```

When the UI opens in your broswer, click any row on the table to open the Contact details page showing the details of the contact.

Press `F12` on your keyboard the dev tools. Switch to the responsive mode and resize the window. You will see how the page adapts to different screen sizes.

Let's continue.

At Line 6, we introduce a `q-toolbar` at the top part of our page. The toolbar contains a `back` button on the left side and two other buttons on the right side. At Line 12, we add a `click` event listener to the `back` button so that we can go back to the previous view easily. The event listener called `$router.go(-1)` which simulates going back `one` step in our history. Read more that `$router.go()` [here](https://router.vuejs.org/guide/essentials/navigation.html#router-go-n).

At Line 41, we interpolate the `fullName` of the contact. At Line 50, we interpolate the `jobDescription` of the contact. At Line 54, we use `v-for` to loop over the number `3` and display three buttons which mimics the groups of the contact. These groups will be implemented when we have a backend server for the application.

At Line 71, we have toolbar which contains three buttons for starring, showing more options, and editing a contact. These buttons will be implemented when we have the backend server for the app. The `edit` button will be implemented soon.

At Line 78, we introduce another row which wraps our `Contact Details` list. The `list`  starts at Line 80 and makes use of Quasar's `q-list` [component](https://quasar.dev/vue-components/list-and-list-items#introduction). We use a `v-for` directive to loop over our `contactData` array. At Line 87, we check that `item.text` exist or `item.text` is a `string` or `item.text` is an array. The `v-ripple` directive on `q-item` give each list item a `click` event capability and ripple effect when clicked.

At Line 98, we render the icon for the list item with `:name="item.icon"`

At Line 104, we render the block if `item.text` is an array:

```html
                <q-item-section v-if="Array.isArray(item.text)">
                  <q-item-label class="text-caption" caption>{{
                    item.label
                  }}</q-item-label>
                  <q-item-label
                    v-for="(line, index) in item.text.filter((l) => l)"
                    :key="'item_line_' + index"
                    :lines="
                      line?.clampLines && line?.clampLines !== 'none'
                        ? line.clampLines
                        : line?.clampLines && line?.clampLines === 'none'
                        ? undefined
                        : 1
                    "
                    >{{ line }}</q-item-label
                  >
                </q-item-section>
```

The `caption` for the item is renders with the first `q-item-label` component by interpolating `item.label`. Then we loop through `item.text` to render the second `q-item-label`. Before looping, we filter out any falsy value from the `item.text` array within the `v-for` directive: `item.text.filter((l) => l)`. The `lines` prop on the second `q-item-label` is used to display ellipsis if there is no sufficient space to display all the text for the label. [Read more that it here](https://quasar.dev/vue-components/list-and-list-items#qitemlabel-api). If `line.clampLines` is set and it is not equal to `none`, we make use of that value. If `line.clampLines` is set and the value is `none`, we return `undefined` which mimics the default behaviour when the `lines` prop is not set at all. Else, we use `1` as the value of the `lines` prop.

At Line 121, we check if the `item.text` is a string and render the block if `true`. This one is simpler but similar to the rendering when `item.text` is an array as described above.

At Line 137, we render the side `q-item-section` which shows extra information for our items. It is used to display a badge (`q-badge`) when `item.side` and `item.sideColor` is set. It is also used to display links when `item.linkAs` is set.

Lastly, there is a sticky button containing an `edit` button which displays in responsive mode when the window is not greater than the `sm` breakpoint. See Line 175.

This concludes our discussions for this lesson.

Save all your files, commit and merge with the master branch.

```bash
git add .
git commit -m "feat(ui): complete design of the contact details page"
git push origin 08-designing-the-contact-detail-page
git checkout master
git merge master 08-designing-the-contact-detail-page
git push origin master
```

In the next lesson, we will create the Contact edit page/form.


*This document was generated from the live article page on https://ndianabasi.com/technical-blog/article/kf9pz97n63fvgfrcy5viwzrg/designing-the-contact-details-page-full-stack-google-contacts-clone-with-adonis-js-node-js-and-quasar-vue-js • 2026-06-07*
