2 Major issues I am having with navigating back


#1

Hi there,

I have a Framewrok7 + Vue + Webpack Project that I am working on and I have 2 major issues that I have been trying for a week to solve.

The first issue is when using standard back link as follows;

 <f7-nav-left back-link="Back"></f7-nav-left>

quite often it will jump back 2 pages and the page is off to the left leaving the right 30% of the page white and is totally unresponsive. It seems completely random when it happens but never gives an error or any information in the console. The only hint I’ve been able to find is the ‘page-current’ is my login screen when the pushState url is displaying as /account/ when in reality I went back to /menu/ but it kicks me to /account/.

The second issue I am having is as follows… I have a menu page with categories, click on a category and it takes you to a dynamic category page using the categoryID, now if I edit the category title on this page and go back to the menu page, the category does not have the updated information, i’ve tried almost every suggestion on the internet regarding this one, but to no avail. This is actually a big issue on most of my pages as on some pages the user can delete an item but then when navigating back it will still be there in the list from when the page was first rendered. The data is being set in the mounted function and tried re setting the information on ‘pageReInit’ events etc.

I don’t know how to add a project of this size to a codePen or JSFiddle but I have a private github repo with the whole project in it if anybody would be kind enough to take a look? I can add you as a collab if you give a github email.

Feels like the first issue is some JS error that I am blind to and the second feels like my lack of Vue knowledge.

Any help would be greatly appreciated!

Many Thanks,
Liam


#2

Hi,

Regarding your second problem, I expect that it is a problem of firing setState() at the proper moment. I suggest you build a simplified app that only contains your category list and the follow up page which should fit in CodePen. This will make it easier for you to discover the “right” way to handle dynamically the category page and makes it easier for others to assist you.

Kind regards, Erik


#3

Hi Erik,

Thanks for your speedy & helpful reply, all of my pages are in separate files so it would be hard to do this. I will post the contents of my category.vue page for you to have a look at. I am not using a setState function in my page so perhaps this is the problem? Any insight would be greatly appreciated.

<template>
  <f7-page>
    <f7-navbar sliding>
      <f7-nav-left back-link="Back">
      </f7-nav-left>
      <f7-nav-title>{{category.title}}</f7-nav-title>
      <f7-nav-right>
        <f7-link sortable-toggle=".sortable">Reorder</f7-link>
      </f7-nav-right>
    </f7-navbar>
    <f7-block>
      <p>Click 'Reorder' at the top to enable sorting of the items in this category.</p>
      <f7-button :href="`/cat/edit/${category.id}/`">Edit '{{category.title}}' Info</f7-button>
    </f7-block>
    <f7-block-title>New Item</f7-block-title>
    <f7-list>
      <f7-list-input label="Item Name" type="text" placeholder="Item Name" clear-button :value="title" @input="title = $event.target.value"></f7-list-input>
      <f7-list-input label="Description (Optional)" type="text" placeholder="Description (Optional)" clear-button
        :value="description" @input="description = $event.target.value"></f7-list-input>
      <f7-list-input label="Item Price" type="text" placeholder="Item Price" clear-button :value="price" @input="price = $event.target.value"></f7-list-input>
      <f7-list-button @click="createItem">Create Item</f7-list-button>
    </f7-list>
    <f7-block v-if="items.length < 1">
      <p style="text-align:center;">You don't have any items to show in this category, add an item using the form above</p>
    </f7-block>
    <f7-block-title v-if="items.length > 0">Your Items</f7-block-title>
    <f7-list media-list sortable @sortable:sort="onSort" id="items-list" >
      <f7-list-item v-for="item in category.items" :key="`item${item.id}`" :title="item.title" :subtitle="item.description"
        :after="`£${item.price}`" :link="`/item/${item.id}/`" :id="item.id"></f7-list-item>
    </f7-list>
  </f7-page>
</template>
<script>
  export default {
    props: {
      categoryID: String
    },
    mounted() {
      const self = this;
      const app = self.$f7;
      const store = self.$store;
      self.category = self.$store.getters.getCategory(self.categoryID);
      self.token = store.state.access_token;
      if (store.state.access_token == "" || store.state.access_token == null) {
        self.$f7router.navigate("//");
      }
    },
    data() {
      return {
        token: "",
        category: {},
        items: {},
        title: "",
        description: "",
        price: '0.00',
      };
    },
    methods: {
      onSort(e) {
        // Sort data
        console.log(e.target.id);
        console.log(e.detail);
        const self = this;
        const app = self.$f7;
        const store = self.$store;
        var token = store.state.access_token;
        var newOrder = [];
        self
          .$$("#items-list")
          .find("li")
          .each(function () {
            newOrder.push(self.$$(this).attr("id"));
          });
        console.log(newOrder);
        app.request({
          url: "https://dash.appliam.com/api/menu/items/reorder",
          headers: {
            Authorization: "Bearer " + token
          },
          method: "POST",
          data: {
            order: newOrder
          },
          success: function (data, status, xhr) {
            console.log(data);
            data = JSON.parse(data);
            if (data.success) {
              console.log("Menu items order updated");
              store.state.items = data.items;
            } else {
              console.log("Something went wrong updating the order of items");
            }
          }
        });
      },
      compare(a, b) {
        if (a.order < b.order) return -1;
        if (a.order > b.order) return 1;
        return 0;
      },
      createItem() {
        const self = this;
        const app = self.$f7;
        const store = self.$store;
        var token = store.state.access_token;
        var url = "https://dash.appliam.com/api/menu/item/new";
        if (self.title != '') {
          app.request({
            url: url,
            headers: {
              Authorization: "Bearer " + token
            },
            method: "POST",
            data: {
              title: self.title,
              description: self.description,
              price: self.price,
              category_id: self.categoryID
            },
            success: function (data, status, xhr) {
              console.log(data);
              data = JSON.parse(data);
              if (data.success) {
                console.log("Menu Item added!");
                store.state.menu = data.menu;
                store.commit("update");
                var category = self.$store.getters.getCategory(self.categoryID);
                self.category = category;
                self.items = category.items.sort(self.compare);
                self.title = "";
                self.description = "";
                self.price = "0.00";
              } else {
                console.log(
                  "Something went wrong adding the item"
                );
              }
            }
          });
        } else {
          //TOAST
        }
      }
    }
  };
</script>

#4

Hi Erik,

Do you know of any function that will run every time the page comes into view? Even via the back button?

Kind Regards,

Liam


#5

Hi Liam,
First my apologizes, you don’t need to use setState() because you are using Vue. :zipper_mouth_face:

The core F7 Page events are at your disposal, such as init, re-init and beforein to refresh a page with router.refreshPage() if necessary.

The router option ignoreCache should be (if not already) set to true

Erik


#6

Hi Erik,

After doing some extensive reading into vuex, I realised I was doing it all wrong directly accessing the state from within my pages, just spent all morning writing actions and mutations for my store, so the second issue is fixed!

The first issue is still happening, now with a random chance of the navbar on the menu page becoming blank, even though in the DOM the navbar and its children are all present.

I have made a video to see if you or @nolimits4web could figure out what it looks like and the best place for me to be looking!

https://drive.google.com/open?id=17NkHUyS5_xi6GIMzqL359uyZD6ZJDTt7

Kind Regards,

Liam


#7

This is now my menu page… (sorry for the scruffy console logs & traces of my frustration in debugging :slight_smile: )

<template>
  <f7-page @page:reinit="init">
    <f7-navbar title="Menu" back-link="Back" sliding>
      <f7-nav-right>
        <f7-link sortable-toggle=".sortable">Reorder</f7-link>
      </f7-nav-right>
    </f7-navbar>
    <f7-block>
      <p>Here is where you can edit your apps food menu. Click 'Reorder' at the top to enable sorting of the menu
        categories order as it will appear in your app.</p>
    </f7-block>
    <f7-block-title>New Category</f7-block-title>
    <f7-list>
      <f7-list-input label="Category Name" type="text" placeholder="Category Name" clear-button :value="title" @input="title = $event.target.value"></f7-list-input>
      <f7-list-input label="Description (Optional)" type="text" placeholder="Description (Optional)" clear-button
        :value="description" @input="description = $event.target.value"></f7-list-input>
      <f7-list-button @click="createCategory">Create Category</f7-list-button>
    </f7-list>
    <f7-block-title>Menu Categories</f7-block-title>
    <f7-list media-list sortable @sortable:sort="onSort" id="categories" v-if="categories.length > 0">
      <f7-list-item v-for="cat in categories" :key="`category${cat.id}`" :title="cat.title" :subtitle="cat.description"
        :link="`/category/${cat.id}/`" :id="cat.id">
      </f7-list-item>
    </f7-list>
    <f7-block>
      <p>Below are your item options categories, these hold item extras e.g. Pizza Base, Kebab Salad</p>
    </f7-block>
    <f7-block-title>Option Categories <f7-link href="/new-option-category/" style="float:right;">New</f7-link>
    </f7-block-title>
    <f7-list media-list id="option_categories" v-if="categories.length > 0">
      <f7-list-item v-for="cat in option_categories" :key="`option-category${cat.id}`" :title="cat.title" :subtitle="catSubtitle(cat)"
        :link="`/option/category/${cat.id}/`">
      </f7-list-item>
    </f7-list>

  </f7-page>
</template>
<script>
  export default {
    mounted() {
      this.init();
    },
    data() {
      return {
        token: '',
        categories: [],
        option_categories: [],
        title: "",
        description: "",
      };
    },
    methods: {
      init() {
        console.log("MENU PAGE INITIALISING!!");
        const self = this;
        const app = self.$f7;
        const store = self.$store;
        self.token = store.getters.getAccessToken;
        if (self.token == "" || self.token == null) {
          console.log("NO ACCESS TOKEN => NAVIGATING");
          self.$f7router.navigate('//');
        } else {
          console.log("ACCESS TOKEN FOUND!!");
        }
        self.option_categories = store.getters.getOptionCategories;
        self.categories = store.getters.getCategories;
      },
      onSort(e) {
        console.log(e.target.id);
        console.log(e.detail);
        const self = this;
        const app = self.$f7;
        const store = self.$store;
        var newOrder = [];
        self.$$('#categories').find('li').each(function () {
          newOrder.push(self.$$(this).attr('id'));
        });
        console.log(newOrder);
        app.request({
          url: "https://dash.appliam.com/api/menu/cat/reorder",
          headers: {
            Authorization: "Bearer " + self.token
          },
          method: "POST",
          data: {
            order: newOrder
          },
          success: function (data, status, xhr) {
            console.log(data);
            data = JSON.parse(data);
            if (data.success) {
              console.log('Menu Categories order updated');
              self.$root.getMenu();
            } else {
              console.log('Something went wrong updating the order of categories');
            }
          },
        });
      },
      catSubtitle(cat) {
        let sub = "";
        if (cat.required) {
          sub += 'Required';
        }
        if (cat.unique) {
          if (sub != '') {
            sub += ' & Unique';
          } else {
            sub += 'Unique';
          }
        }
        return sub;
      },
      createCategory() {
        const self = this;
        const app = self.$f7;
        const store = self.$store;
        var url = "https://dash.appliam.com/api/menu/cat/new";
        app.request({
          url: url,
          headers: {
            Authorization: "Bearer " + self.token
          },
          method: "POST",
          data: {
            title: self.title,
            description: self.description
          },
          success: function (data, status, xhr) {
            console.log(data);
            data = JSON.parse(data);
            if (data.success) {
              console.log('Menu Category added!');
              store.commit('storeMenu', data.menu);
              self.categories = store.getters.getCategories;
              self.title = "";
              self.description = "";
            } else {
              console.log('Something went wrong adding the category');
            }
          },
        });
      }
    }
  };
</script>

#8

First of all this can definitely break the router:

But look like it is not the case. What is loggin “ROOT GET MENU FUNCTION” in your video? Looks like it breaking it?

Such issue can happen, if you try to update page element during transition, e.g. add/remove class or something else that can break routing


#9

Hi Vladimir,

Thank you for the speedy reply! I have a function in the Vue instance that gets the latest version of the menu from my API, is this a bad idea? It was because this was needed in many pages of my app and I didn’t want to copy past on every page in case of a change that needs making further down the line. here is my main.js

import Vue from 'vue'
import Framework7 from 'framework7/framework7.esm.bundle.js';
import Framework7Vue from 'framework7-vue/framework7-vue.esm.bundle.js';
import Framework7CSS from 'framework7/css/framework7.css'
import Framework7Icons from 'framework7-icons/css/framework7-icons.css'
import MaterialIcons from 'material-design-icons/iconfont/material-icons.css'
import FontAwesome from 'font-awesome/css/font-awesome.css'
// import AppStyles from './assets/sass/main.scss'
import app from './main.vue'
import store from './assets/vuex/storage.js'
Framework7.use(Framework7Vue)
export default new Vue({
  el: '#app',
  store,
  render: c => c('app'),
  components: {
    app
  },
  methods: {
    getMenu() {
      console.log('ROOT GET MENU FUNCTION');
      const self = this;
      const app = self.$f7;
      const store = self.$store;
      const app_id = store.getters.getAppID;
      app.request({
        url: "https://dash.appliam.com/api/menu/" + app_id,
        method: "GET",
        success: function (data, status, xhr) {
          data = JSON.parse(data);
          store.commit('storeMenu', data);
        },
      });
      app.request({
        url: "https://dash.appliam.com/api/menu/item/option/categories/" + app_id,
        method: "GET",
        success: function (data, status, xhr) {
          data = JSON.parse(data);
          store.commit('storeOptionCategories', data);
        },
      });

    }
  }
});

#10

Not a bad idea, but something breaking the router. If you disable that getMenu method, is the issue is still there?


#11

I changed the method to just be a console.log(‘nothing going on here’)… still the issue exists, so then after I recorded this second video I even commented out all the calls to self.$root.getMenu(), and the issue still persists.

I have noticed a pattern though as you will be able to see in the video. If I navigate 1 page deep from the menu page and go back the navbar becomes blank even though the elements are in the DOM? If you then navigate 1 page deep again and try to go back, the 20% white page error then occurs. If you start from scratch and navigate more than 1 page deep away from the menu page it will always go back to account page with 20% white.

Do you have a github email so I could give you access to the full project? If you don’t have time, I understand you’re a busy man.

Here is the video showing the navbar strangeness: https://drive.google.com/open?id=13CO2Yq8zdy7epnA7IQVF7PpYIBYSCKHr

Kind Regards,
Liam


#12

Ok, checked repo. The issue is that you have router.navigate('/account/'); in mounted() hook in your home.vue page. And i see you have such logic in many places, make sure to remove it everywhere as trying to navigate router in mounted is not allowed. To check auth, it is better to use async routes or beforeEnter

http://framework7.io/docs/routes.html#route-before-enter-leave
http://framework7.io/docs/routes.html#async-route

You need to do required Auth check in such methods and resolve route to requested one if allowed or resolve to login page otherwise


#13

Thank you Vladimir for taking the time to look into this, it is very much appreciated!

I will spend the rest of today fixing my routes and implementing a correct auth system using the methods you have mentioned.

Many Thanks,
Liam


#14

Thank you so much! It now makes a lot more sense… I just changed 1 route and all problems have gone…

{
    path: '/',
    async(routeTo, routeFrom, resolve, reject) {
      if (store.getters.userLoggedIn) {
        resolve({ component: Account })
      } else {
        resolve({ component: Login })
      }
    }
  },

Now I will remove this from all pages:

if (self.token == "" || self.token == null) {
    self.$f7router.navigate('//');
}

Just a quick clarification… The following usage of the router navigation is fine after a user logs in?

success: function (data, status, xhr) {
                        data = JSON.parse(data);
                        if (data.success) {
                            self.$store.commit('storeUserLogin', data);
                            router.navigate('/account/');
                        }
                        else{
                            self.$f7.dialog.alert("This account is not the owner of an app");
                        }
                    }

#15

Yes, calling in that place is fine