Strange and different behavior on android

Hello,

I encounter a strange bug. Indeed I created a form in a modal (itself in a component).
When I use this feature on the browser I have no problem, but on android, the data is not saved in the store (vueX).
I don’t understand why. (and I can’t debug it)
Would you have an idea?

the component :

<template>
    <ion-modal :is-open="props.isOpen">
        <ion-header>
            <ion-toolbar>
                <ion-title>{{ formatedSelectedDate }}</ion-title>
                <ion-buttons slot="end">
                    <ion-button @click="setOpen(false)">Fermer</ion-button>
                </ion-buttons>
            </ion-toolbar>
        </ion-header>
        <ion-content class="ion-padding ion-text-center">
            <div id="container">
                <form @submit.prevent="saveNote">
                    <ion-label>Votre poids</ion-label>
                    <ion-input
                        type="number"
                        placeholder="65"
                        name="weight"
                        v-model="formData.weight"
                    ></ion-input>
                    <ion-label>Comment vous sentez vous ?</ion-label>
                    <ion-textarea
                        placeholder="Je me sens bien aujourd'hui"
                        :auto-grow="true"
                        name="feeling"
                        v-model="formData.feeling"
                    ></ion-textarea>
                    <ion-label>Autre chose ?</ion-label>
                    <ion-textarea
                        placeholder="J'ai mangé des carottes"
                        :auto-grow="true"
                        v-model="formData.note"
                    ></ion-textarea>
                    <ion-button type="submit" expand="block">
                        Valider
                    </ion-button>
                </form>
            </div>
        </ion-content>
    </ion-modal>
</template>

<script setup lang="ts">
import { defineProps, defineEmits, reactive } from "vue";
import {
    IonModal,
    IonContent,
    IonHeader,
    IonToolbar,
    IonTitle,
    IonButtons,
    IonButton,
    IonLabel,
    IonInput,
    IonTextarea,
} from "@ionic/vue";
import { computed, ref } from "vue";
import fr from "date-fns/locale/fr";
import { format, parseISO, getUnixTime } from "date-fns";
import store from "@/store";

const props = defineProps({
    isOpen: Boolean,
    selectedDate: String,
    formData: Object,
    isNewNote: Boolean,
});

const formData = reactive(ref(props.formData) as object);

const emit = defineEmits(["update:isOpen", "onSave"]);

const formatedSelectedDate = computed(() => {
    return format(parseISO(props.selectedDate as string), "EEE d LLLL", {
        locale: fr,
    });
});

function setOpen(isOpen: boolean) {
    emit("update:isOpen", isOpen);
}

function saveNote() {
    const note = {
        date: getUnixTime(parseISO(props.selectedDate as string)),
        weight: parseInt(props.formData?.weight),
        feeling: props.formData?.feeling,
        note: props.formData?.note,
        isNewNote: props.isNewNote,
    };
    store.dispatch("saveNote", note);
    emit("onSave", note);
    setOpen(false);
}
</script>

the view :

<template>
    <ion-page>
        <ion-content
            :fullscreen="true"
            class="ion-padding-horizontal ion-padding-top"
        >
            <section>
                <ion-datetime
                    id="datetime"
                    locale="fr-FR"
                    v-model="selectedDate"
                    :first-day-of-week="1"
                    presentation="date"
                    @ionChange="checkDateNote(selectedDate)"
                >
                </ion-datetime>
                <ion-button
                    expand="block"
                    class="ion-margin-vertical ion-padding-vertical"
                    v-if="dayNote.note == ''"
                    @click="setOpen(true, true)"
                    >Ajouter une note</ion-button
                >
                <div v-else>
                    <ion-card>
                        <ion-card-content>
                            <b>Votre poids :</b> {{ dayNote.weight }}
                            <br />
                            <b>Votre sentiment :</b><br />{{ dayNote.feeling }}
                            <br />
                            <b>Autre chose :</b><br />{{ dayNote.note }}
                        </ion-card-content>
                        <ion-button
                            fill="clear"
                            class="ion-float-right"
                            @click="changeDayNote"
                            >Modifier</ion-button
                        >
                    </ion-card>
                </div>
            </section>
        </ion-content>
        <note-form
            v-model:isOpen="isOpen.isOpen"
            :selectedDate="selectedDate"
            :formData="formData"
            @onSave="saveNote"
            :isNewNote="isNewNote.isNew"
        />
    </ion-page>
</template>

<script lang="ts">
import { computed, defineComponent, reactive, ref } from "vue";
import {
    IonPage,
    IonContent,
    IonDatetime,
    IonButton,
    IonCard,
    IonCardContent,
} from "@ionic/vue";
import { parseISO, isSameDay, fromUnixTime, formatISO } from "date-fns";
import { useStore } from "vuex";
import NoteForm from "@/components/NoteForm.vue";

export default defineComponent({
    name: "Tab4Page",
    ionViewWillEnter() {
        this.$store.dispatch("loadAllNotes");
    },
    components: {
        IonContent,
        IonPage,
        IonDatetime,
        IonButton,
        IonCard,
        IonCardContent,
        NoteForm,
    },
    setup() {
        const store = useStore();
        let dayNote = reactive({
            note: "",
            feeling: "",
            weight: ref(),
        });
        const selectedDate = ref();
        const allNotes = computed(() => store.state.allNotes);
        const isOpen = reactive({ isOpen: false });
        const formData = ref({ note: "", feeling: "", weight: ref() });
        const isNewNote = reactive({ isNew: false });

        function checkDateNote(value: string) {
            dayNote.note = "";
            dayNote.feeling = "";
            dayNote.weight = null;
            if (allNotes.value != null) {
                allNotes.value.forEach((element: any) => {
                    if (
                        isSameDay(fromUnixTime(element.date), parseISO(value))
                    ) {
                        dayNote.note = element.note;
                        dayNote.feeling = element.feeling;
                        dayNote.weight = element.weight;
                    }
                });
            }
        }

        function saveNote(newNote: any) {
            dayNote.weight = newNote.weight;
            dayNote.feeling = newNote.feeling;
            dayNote.note = newNote.note;
        }

        function changeDayNote() {
            formData.value.weight = dayNote.weight;
            formData.value.feeling = dayNote.feeling;
            formData.value.note = dayNote.note;
            setOpen(true, false);
        }

        function setOpen(val: boolean, isNew: boolean) {
            isNewNote.isNew = isNew;
            !selectedDate.value
                ? (selectedDate.value = formatISO(new Date()))
                : null;
            isOpen.isOpen = val;
        }

        return {
            dayNote,
            selectedDate,
            checkDateNote,
            allNotes,
            changeDayNote,
            setOpen,
            isOpen,
            saveNote,
            formData,
            isNewNote,
        };
    },
});
</script>

<style scoped>
#container {
    text-align: center;
    position: relative;
    top: 50%;
    transform: translateY(-70%);
}
</style>

the store :

import { createStore } from "vuex";
import { isSameDay, fromUnixTime } from 'date-fns'
import { Storage } from '@ionic/storage';

const ionicStorage = new Storage();
ionicStorage.create();

export default createStore({
    state() {
        return {
            allNotes: [{
                id: 0,
                date: 1666892573,
                weight: 65,
                feeling: "Je me sens bien aujourd'hui",
                note: "Ceci est une note d'exemple, elle disparaitra quand vous aurai fait votre première note",
            },
            ]
        }
    },
    getters: {
        sortedAllNotes(state) {
            return state.allNotes.sort((a, b) => a.date - b.date)
        }
    },
    mutations: {
        SAVE_NEW_NOTE(state, newNote) {
            state.allNotes.push(newNote)
        },
        UPDATE_NOTE(state, note) {
            const noteIndex = state.allNotes.findIndex(element => isSameDay(fromUnixTime(element.date), fromUnixTime(note.date)))
            state.allNotes[noteIndex].weight = parseInt(note.weight)
            state.allNotes[noteIndex].feeling = note.feeling
            state.allNotes[noteIndex].note = note.note
        },
    },
    actions: {
        saveNote({ commit, state }, note) {
            if (note.isNewNote === true) {
                delete note.isNewNote
                commit('SAVE_NEW_NOTE', note)
                ionicStorage.set('allNotes', JSON.stringify(state.allNotes));
            } else {
                delete note.isNewNote
                commit('UPDATE_NOTE', note)
                ionicStorage.set('allNotes', JSON.stringify(state.allNotes));
            }
        },
        async loadAllNotes({ state }) {
            if (state.allNotes.length <= 1) {
                if (JSON.parse(await ionicStorage.get('allNotes')) != null) {
                    state.allNotes = JSON.parse(await ionicStorage.get('allNotes'))
                }
            } else {
                if (state.allNotes[0].id == 0) {
                    state.allNotes.shift();
                }
            }
        },
    }
});
1 Like

You say you cannot debug it. Have you tried opening up Chrome DevTools for your Android device (physical or emulator)? You can acccess DevTools for a device by going to chrome://inspect/#devices in Chrome.

1 Like

I tried but I have nothing in the console. And I don’t have access to the devtools view extension

I had this issue before where a string value was not changing despite being reactive, on Android WebView only.

I ended up wrapping the value in an array[value] and it fixed it, probably because arrays are passed by reference.

1 Like

Maybe try to do some console.log to see where exactly it is not working. To @mirko77 point, maybe the store is being updated but just not showing due to a reactive issue.

I would also check DevTools → Application → Storage to see if the values are being saved there too (where Ionic Storage is saving them).

2 Likes

Hi,

I finally found the solution. Well, the solutions because it’s a combination of things.
At first it seems that the architecture where you put a modal in a child component doesn’t work well on mobile. It’s better to put a modal in the parent and the content of the modal in the component, like this:

<!-- Wrong deisgn  -->
<!-- Parent -->
<template>
    <MyComponent :is-open="isOpen" />
</template>

<!-- Child component -->
<template>
    <ion-modal :is-open="isOpen"> </ion-modal>
</template>

<!-- Working design :  -->

<!-- Parent -->
<template>
    <ion-modal :is-open="isOpen">
        <MyComponent />
    </ion-modal>
</template>

<!-- Child component -->
<template>
    <ion-content> </ion-content>
</template>

Then I transformed all my const note = ref({object}); or reactive into objects only const selectedDate = {object}; it made my life much easier.

Then there were also some errors in the code, but the problem was not from there.

Anyway, thanks for your help!

PS : console.log works but not the vueDevTool unfortunately