[SOLVED] [V2] Stacked Pages

Hello everyone. First of all thank you for the great job you have done with this framework.

I have been working with F7 V1 for some time and I have decided to migrate to F7 V2 and VueJS.
I have taken the template F7 - Vue - Webpack - Cordova and can successfully run the project.

The problem that I have not been able to solve is related to the inline pages (stacked pages).
I need the pages to be loaded into the DOM only once. I have tried setting stacked-pages in true to the main view but the screens are loaded again and again over and over again.
I also tried adding the stacked property to the pages but it did not work either.
Here is the code:

main.vue
https://pastebin.com/KhnicDMP

home.vue [form.vue and about.vue have the same code]
https://pastebin.com/33r0Jb2B

panel-left.vue
https://pastebin.com/0qtivd7Y

routes.js
https://pastebin.com/YV3i8bGB

main.js
https://pastebin.com/cy2hRKxQ

How can I show the pages that are already loaded in the DOM?
I understand one can navigate to a page by page Name or the ref but I could not achieve.
Could someone help me with a concrete example?
Thanks in advance!

I do not understand point why you need to load pages into DOM ? whats the logic for Stacked Pages ?? I dont know much detail about it but router mechanism brings any page you want, but it needs to be imported during initialization which is done in the example below.

I think this will work pretty well for you,
https://github.com/pvtallulah/base-vue-f7v2-template

Thank you very much for your response, rastek.

I have tried the indicated project but I still do not get the expected result and I can not understand how to achieve it.

In your project, screens are loaded into the DOM again and again, as many times as you navigate to them from the side menu. In the DOM only 2 screens are stacked at the most and then they are stepped on because the stacked-pages property is set to false.
If we set staked-pages to true, the behavior is similar, that is, the screens are loaded in the DOM every time I navigate to them but with the difference that they are all stored in the DOM, arriving to have the same screen repeated times.

What I try to achieve is that the screens are loaded in the DOM as I go browsing and in case you navigate to a screen that has already been loaded in the DOM, the existing one is displayed, preventing it from being loaded on the stack as a new.
To make it better understood, I leave an example flow of what I want to achieve:
** The user is already logged in and is on the Home screen**
Stack: [Home screen]
1. From the side menu access About. About screen is loaded on the stack
Stack: [About screen - Home screen]
2. From the side menu access Form. Form screen is loaded on the stack
Stack: [Form screen – About screen – Home screen]
3. From the side menu access About. About screen is NOT loaded on the stack, the one that has already been loaded in step 1 is displayed.
Stack: [About screen – Form screen – Home screen]

Any suggestions to achieve what was described above?
Tell me if more information about the context is necessary.
Thank you very much.
Regards, Fabricio.

First its not my template, it belongs to Pvtallulah around the forum, you can ask him spesific questions

What you mean screens are loaded into the DOM again and again ?? I also use that template , I didnt knew about it, how you follow it ? Is that means after a while applications getting started slowly because too many pages loaded ?

Thanks for the reply, rastek.

I mean that the Init event of the pages is triggered every time they show up when it should fire only once.

In the attached image you can see that the pages are loaded repeatedly in the DOM. The result sought is like the one described in the example flow above.

Istn it expected that Init event of the pages is triggered every time they show up ? meanwhile where is that init code ? for example I dont use init for a component in vue, I just use created/computed if I need, maybe I am missing something here.

btw, right side is you UI ? or is it some kind of develop screen ? how you open it ?

is that repeated list shows they are all loaded ? or it just ordered list of pages opened one after another ? like some logging

Here you can see the Page Events: http://framework7.io/vue/page.html

It’s an navigator extensión (Mozilla in my case) called “Vue.js devtools”.

The list shows all the pages that have been loaded on the stack. The behavior sought is that they do not repeat themselves and remain on the stack once they have been loaded.

Hi @fabricio,
So you want stacked pages, but instead of creating a new instance of a page, load the page that’s is already created right?
I dont know how to achive that, will try it.
Just curious, why you need stacked pages?

Exactly, @pvtallulah.

I need the stacked pages to maintain their status in case the user navigates to another page (from the side menu) and at some point decides to return.

Thank you for your reply and for having shared the template code “base-vue-f7v2-template”.

I am available for any additional information you need.

@fabricio i try to make it work as you ask, and i couldn’t, the pages keep stacking on DOM.
i even try to prevent route but couldn’t succeed. even i call reject() but f7 continues to the route, so maybe reject() dosnt work?

router reject method:

...
  {
    path: '/about/',
    component: AboutPage,
    async(routeTo, routeFrom, resolve, reject) {
      if (this.history.indexOf(routeTo.path) >= 0) {
        console.log('page exist on stack')
        reject()
      } else {
        console.log('page DONT exist on stack')
        resolve()
      }
    }
  },
...

Set params on f7 init gives an error:

...
  framework7: {
    id: 'com.base.app', // App bundle ID
    name: 'BaseApp', // App name
    theme: 'ios', // MD theme
    // App routes
    routes: Routes,
    view: {
      stackPages: true,
      uniqueHistory: true,
      // If commented pushState apps works fine, but pages keep stacking
      pushState: true
    }
  },
...

[Vue warn]: Duplicate keys detected: ‘1529714183634’. This may cause an update error.

But if i set it in mounted ()
apps load with no error;

  mounted () {
    this.$f7.view.main.params.stackPages = true
    this.$f7.view.main.params.uniqueHistory = true
    this.$f7.view.main.params.pushState = true
  },

But pages keep stacking.

I dont know if this is how f7 is design to work or im doing something wrong. Since i never used Stacked Pages.
Maybe @nolimits4web can clarify the issue.

Thank you very much @pvtallulah. Honestly, I had not thought of trying that alternative.
I was working on his example and it occurred to me to do something like this:
{
path: ‘/about/’,
name: ‘page-about’,
async(routeTo, routeFrom, resolve, reject) {
if (this.history.indexOf(routeTo.path) >= 0) {
console.log(‘page exist on stack’);
// Navigate to page by pageName
//resolve({ pageName: ‘page-about’ });
} else {
console.log(‘page DONT exist on stack’)
resolve({component: AboutPage});
}
}

The problem is that I do not know how to navigate via pageName. Will this be possible? Can you think of a way to solve it?

It would be important to have the opinion of @nolimits4web , meanwhile I will continue trying.

If you enable stackPages: true for View, it will keep pages in DOM while you navigate forward.

But if you need to reuse all pages and load them all at once, then it requires different layout and all pages must be loaded to view initially and not loaded by router:

<f7-view :stackPages="true">
  <f7-page name="home" stacked>...</f7-page>
  <f7-page name="about" stacked>...</f7-page>
  <f7-page name="other" stacked>...</f7-page>
</f7-view>

And in routes you need to specify them by page name:

{
  path: '/about/',
  pageName: 'about',
},
2 Likes

Thank you very much Vladimir, I have been able to solve the issue with the indicated Layout.

Take this opportunity to ask a question, can I keep the following structure in the page components?

user-list.vue

Summary
<template>
  <f7-page name="user-list" stacked>
    <f7-navbar>This text will be hidden
      <f7-nav-left>
        <f7-link icon-if-ios="f7:menu" icon-if-md="material:menu" panel-open="left"></f7-link>
      </f7-nav-left>
      <f7-nav-title>User list</f7-nav-title>
    </f7-navbar>
    <div class="list media-list" v-if="users.length != 0">
      <ul>
        <li v-for="user in users">
          <a :href="user | toUrl" class="item-link item-content">
            <div class="item-media lazy-blur-parent">
              <img
              :data-src="user.picture.large"
              :src="user.picture.thumbnail"
              class="lazy lazy-blur"
              width="80"/>
            </div>
            <div class="item-inner">
                <div class="item-title-row">
                  <div class="item-title">{{user.name.first}} {{user.name.last}}</div>
                </div>
                <div class="item-subtitle">{{user.gender}}</div>
                <div class="item-text">{{user.location.street}} - {{user.location.city}} - {{user.location.state}}</div>
            </div>
          </a>
        </li>
      </ul>
    </div>
  </f7-page>
</template>
<script>
  export default {
    data () {
      let users = [];
      return {
        users
      };
    },
    filters: {
      toUrl(user)
      {
        return '/user-list/user/' + user.gender + '/'
      }
    },
    methods: {
      getUsersList () {
        const self = this;
        self.$root.$f7.dialog.progress();
        self.$request.get('https://randomuser.me/api/?results=15',
          function (data, status, xhr) {
            self.users = data.results;
            self.$root.$f7.dialog.close();
            setTimeout(function(){
              self.$root.$f7.lazy.create('img.lazy');
            },1000)
          },
          function (xhr, status) {
            console.log('Error: '+ JSON.stringify(xhr));
            self.$root.$f7.dialog.close();
          },
          "json");
      },
    },
    on:
    {
      pageInit (event, pageData) {
        this.getUsersList();
      }
    }
  };
</script>
<style lang="scss" scoped>
  .md .list
  {
    margin-top: 0px;
  }
  .md .media-list .item-media
  {
    height: 80px;
    margin-top: 14px;
    margin-bottom: 14px;
    padding-top: 0px;
    padding-bottom: 0px;
  }
</style>

app.vue

Summary
<template>
  <!-- App -->
  <div id="app">

    <!-- Statusbar -->
    <f7-statusbar></f7-statusbar>

    <!-- Left Panel -->
    <f7-panel left reveal theme-dark>
      <f7-view url="/panel-left/"></f7-view>
    </f7-panel>

    <!-- Main View -->
    <f7-view id="main-view" :stackPages="true" main>
      <page-home>
      </page-home>
      <page-about>
      </page-about>
      <page-other>
      </page-other>
      <page-user-list>
      </page-user-list>
    </f7-view>

  </div>
</template>

<script>
import Vue from 'vue';

import HomePage from './pages/home.vue';
import AboutPage from './pages/about.vue';
import OtherPage from './pages/other.vue';
import UserList from './pages/user-list.vue';

Vue.component('page-home', HomePage);
Vue.component('page-about', AboutPage);
Vue.component('page-other', OtherPage);
Vue.component('page-user-list', UserList);

export default {
    components:
    {
      HomePage, AboutPage, OtherPage,UserList
    },
    data () {
    }
  }
</script>

This way the page events don’t fire and I want to avoid putting all the code in route.js

I was trying in many ways but I have not succeeded. Is there any way to solve it?

Thank you very much, your work is fantastic.

Just use Vue’s mounted hook instead of pageInit event in this case

The mounted event is launched correctly but I don’t need it to be launched at the beginning of the App (when the component is loaded in the DOM). I need launch it when the screen is first shown.

On the other hand, what happens with the other page events such as “pageBeforeIn”. I can not subscribe to them within the page component?

Thank you very much for your time Vladimir.

you can use any vue lifecycle event. just use the events in any page you want

Use them as events:

<f7-page @page:beforein="onPageBeforeIn">...</f7-page>
{
  methods: {
    onPageBeforeIn() {
      ...
    }
  }
}
1 Like

It works like a charm.

Thank you very much Vladimir, you are the best!

Fabricio is that solve your problem ? My vue + v2 app beside slowness and most of the time screen freezes after a while, I am investigating it maybe its the reason you wrote in the post.

Yes, @rastek. This issue has been fixed.