How to pass a prop to a page [Ionic + Vue 3]

I’m using Ionic + Vue3, and I’m trying to pass a prop from the root app to the page components, with data binding, but it looks like the components will not receive the prop.

This is my root app:

<template>
  <ion-app>
    <!--  Just trying to send data to a child component just like with a regular component -->
    <ion-router-outlet :items="items" />
  </ion-app>
</template>

<script>
import { IonApp, IonRouterOutlet } from '@ionic/vue';
import { defineComponent } from 'vue';

export default defineComponent({
  name: 'App',
  components: {
    IonApp,
    IonRouterOutlet
  },
  data() {
    return {
      items: [
        {
          name: 'Foo',
        },
        {
          name: 'Bar'
        },
      ]
    }
  }
});
</script>

And one of my components:

<template>
    <ion-page>
        <ion-content>
            <ion-list v-for="item in items" :key="item">
              <ion-item>
                <ion-label>{{ item.name }}</ion-label>
              </ion-item>
            </ion-list>
        </ion-content>
    </ion-page>
</template>
<script>
import { IonContent, IonPage, IonList, IonItem, IonLabel } from '@ionic/vue';

export default {
  name: 'Home',
  components: {
    IonContent,
    IonPage,
    IonList,
    IonItem,
    IonLabel,
  },
  props: [
    'items'
  ],
  },
  mounted() {
    console.log(this.items); // outputs: undefined
  }
};
</script>

The ionic list is not rendered, since, as I’ve checked by using the mounted() hook, the items prop is undefined. No value received. I’m aware that <ion-router-outlet> is a special Ionic component, not a pure Vue one, but I read in the docs that it behaves in the same way as <router-link>, and <router-link> supports passing props with data binding. Seems that it’s not the case for <ion-router-link>.

How can I work around this?

Afaik. You cannot pass props from the ionic router but you attach it to a parent component to child you will got this props

ROOT: app.vue
parent page eg: home.vue -> listComponent.vue -> pass props :this="data"
child component eg listComponent.vue -> access props -> make a v-for loop

this typically works

if you have a component your root like header component you can pass props to it but in the router not and also you need to import the component like this.

your script
import List from "../components/listComponent";

your template
<List :key="key" > </List>

Hope it helps…

1 Like

you really want to create a new object/service/compsable that contains the data and allows it to be reactive an let any component access the property without passing it all over the place.

here is a sample project, codesandbox.io that you can look at to see this patten in action
https://codesandbox.io/s/simple-composable-for-sharing-data-j1lx3

here is a video: https://youtu.be/npAdAd3Xb1g

// dataservice.ts
import { ref } from "vue";

const data = ref<any>([new Date().getTime()]);

export default function () {
  const addItem = (item: string) => {
    data.value.push(item);
  };

  return {
    data,
    addItem
  };
}

Then in your component, you get access to the data


<template>
    <ion-page>
        <ion-content>
            <ion-list v-for="item in data" :key="item">
              <ion-item>
                <ion-label>{{ item.name }}</ion-label>
              </ion-item>
            </ion-list>
        </ion-content>
    </ion-page>
</template>
<script>
import { IonContent, IonPage, IonList, IonItem, IonLabel } from '@ionic/vue';

export default {
  name: 'Home',
  components: {
    IonContent,
    IonPage,
    IonList,
    IonItem,
    IonLabel,
  },
  setup(){
     const { addItem, data } = useDataService();
     return {
        data
     }
  }
 
};
1 Like

Thanks both of you.

Aaron: wow you caught me by surprise! I’m not used to Typescript and to all of the ES6 syntax yet. But I get your point.

Anyway, I found a more direct, and in my humble opinion, better option, which was using provide/inject provide/inject, from the root component:

App.vue

<template>
  <ion-app>
    <!--  No need to pass anything to ion-router-outlet anymore! -->
    <ion-router-outlet />
  </ion-app>
</template>

<script>
import { IonApp, IonRouterOutlet } from '@ionic/vue';
import { defineComponent } from 'vue';

export default defineComponent({
  name: 'App',
  components: {
    IonApp,
    IonRouterOutlet
  },
  data() {
    return {
      items: [
        {
          name: 'Foo',
        },
        {
          name: 'Bar'
        },
      ]
    }
  },
  // I had to declare it as a function returning an object in order to access this component data
  provide() {
    return {
      items: this.items
    }
  }
});
</script>

One of the subcomponents (Home.vue). Here I only needed to replace “props” with “inject”:

<template>
    <ion-page>
        <ion-content>
            <ion-list v-for="item in items" :key="item">
              <ion-item>
                <ion-label>{{ item.name }}</ion-label>
              </ion-item>
            </ion-list>
        </ion-content>
    </ion-page>
</template>
<script>
import { IonContent, IonPage, IonList, IonItem, IonLabel } from '@ionic/vue';

export default {
  name: 'Home',
  components: {
    IonContent,
    IonPage,
    IonList,
    IonItem,
    IonLabel,
  },
  inject: [
    'items'
  ],
};
</script>
1 Like

Glad you found something that works for you.

In my opinion inject provide is a solution for data that is not volatile, once you need to start updating the data, adding more business logic and such it is going to get unwieldy…
You are basically just creating a global variable.

It works fine now because this is a small, sample app but when you get to building a client solution, you will be back with using composition api or some other state manager.

I’m not sure if I understood what you mean.

I think you refer to taking advantage of the new composition API (Vue 3) in order to extract all data and business logic from the components, and gather it all in centralized scripts, and then import specific data/functions from them in their suitable components. Am I right? Makes sense.

Speaking about state managers, I understand it would be perfect to use Vuex in this case too. At least if I know data and business logic will grow, since setting up Vuex for just a value is cumbersome.