Vue+Onsen UIでページをpopする際、宛先のページの関数を実行

Created 2018年11月5日15:06
Updated 2018年11月5日15:08
Categories nodejs cordova

Teratailで質問した内容と同じですが、こちらにも書いておきます。

Cordova+Vue+Onsen UIでスマホアプリを作っており、メイン画面からv-ons-navigatorで設定画面をpushするような構造にしていました。

設定画面からpopする際にメイン画面に設定を反映させようと試行錯誤したのですが、これがなかなか正しい方法が分からず結構苦労してしまいました。

コード

index.html

<template id="main">
  <v-ons-navigator swipeable
    :page-stack="pageStack"
    @push-page="pageStack.push($event)"
    @postpop="testFunction"
  ></v-ons-navigator>
</template>

<template id="page1">
  <v-ons-page>
    <v-ons-toolbar>
      <div class="center">Page 1</div>
    </v-ons-toolbar>
    <p style="text-align: center">
      This is the first page {{ showText }}
      <v-ons-button @click="push">Push Page 2</v-ons-button>
    </p>
  </v-ons-page>
</template>

<template id="page2">
  <v-ons-page>
    <v-ons-toolbar>
      <div class="left">
        <v-ons-back-button>Page 1</v-ons-back-button>
      </div>
      <div class="center">Page 2</div>
    </v-ons-toolbar>
    <p style="text-align: center">This is the second page</p>
  </v-ons-page>
</template>

<div id="app"></div>

main.js

var page2 = {
  key: 'page2',
  template: '#page2'
}

var page1 = {
  key: 'page1',
  template: '#page1',
  methods: {
    push () {
      this.$emit('push-page', page2)
    },
    changeValue () {
      this.showText = 'AAA'
    }
  },
  data () {
    return {
      showText: 'Not changed'
    }
  }
}

new Vue({
  el: '#app',
  template: '#main',
  methods: {
    testFunction () {
      // ここでpage1のchangeValue関数を実行したい
    }
  },
  data () {
    return {
      pageStack: [page1]
    }
  }
})

ダメだったこと

v-ons-navigatorのイベントはどのページからでも呼べるので、page2でv-ons-navigatorのイベントを発火させてpage1の関数を実行する

@postpopイベントを基軸に#mainコンポーネント内でpage1のchangeValueを呼ぼうとしましたが、

page1.changeValue()
page1.methods.changeValue()

のどっちもダメでした。

理由は恐らくpage1とpage2がただの変数であり、#mainコンポーネント内で使えるようにコンポーネント化されていないからだと思います。

コンポーネント化するにはvueのcomponentsディレクティブ内に宣言してその宣言通りにHTML内で記述する必要がありますが、v-ons-navigatorはpageStack内のページコンポーネントを自動的に生成するため、親コンポーネントから子コンポーネントに直接アクセスする方法が(調べた限り)見つかりませんでした。

v-bind.syncv-ons-navigator内で共有できる変数を渡す

page1とpage2に直接v-bindを記述できないため参照できませんでした。

解決策

ドキュメントを読んでいると、実はv-ons-navigatorのページに書いてありました。

In order to pass data back to the previous page, we can either use Vuex, an event bus, or simply pass a function that modifies the corresponding context.

// Page A
this.$emit('push-page', {
  extends: pageB,
  onsNavigatorProps: {
    passDataBack(data) {
      this.dataFromPageB = data;
    }
  }
});

つまり、上のコードのように$emitのプロパティとして関数を渡してあげれば、page2で実行できるという事でした。

今回のコードの場合、

this.$emit('push-page', {
  extends: pageB,
  onsNavigatorProps: {
    changeValue: this.changeValue
  }
})

として、戻るボタンでそれを実行するだけです。つまり、

<v-ons-back-button @click="changeValue">戻る</v-ons-back-button>

これだけでOKでした。なんてあっけない・・・

ちなみに、設定が入っているオブジェクトを直接渡したらPC上のデバッグではうまくいきまいたが、スマホ上ではpage1とpage2で同期が行われずうまくいきませんでした。

所感

  • ドキュメントはちゃんと読もう

コメントを投稿

コメント

名無し

ちょっと自分がしたいこととは違いますが、参考になり無事解決しました! ありがとうございました!