質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.33%

  • JavaScript

    17608questions

    JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

  • Monaca

    1013questions

    「Monaca」はiOS、Android、Windows向けのアプリ開発に対応した、Cordovaベースのモバイルアプリ開発プラットフォームです。HTML5、JavaScriptといったWeb標準技術を用いてモバイルアプリ開発を行うことができます。

  • Vue.js

    852questions

    Vue.jsは、Webアプリケーションのインターフェースを構築するためのオープンソースJavaScriptフレームワークです。

  • Onsen UI

    334questions

    HTML5で記述されたモバイルアプリの高速化、およびネイティブアプリライクなUIが作れるフレームワーク。 様々なJavaScriptフレームワークと併せて使用することができます。スマートフォン向けアプリ、Webサイトに必要なアニメーション、UI/UXを実装することが可能になります。

Vue.js+OnsenUIのnavigatorの扱うページで値を共有したい

解決済

回答 4

投稿 編集

  • 評価
  • クリップ 1
  • VIEW 393

tyama_jp

score 9

 前提・実現したいこと

Vueを学習しており、現在はMonacaでアプリの画面遷移について勉強中です。
v-ons-navigatorで管理されたコンポーネント間で共有できる変数を用意したいと考えています。

コンポーネントは
・App.vue (v-ons-navigator所持)
・Page1.vue (最初に表示されるページ)
・Page2.vue (途中でpush、popされるページ)
の三つです。

App.vueに作ったtodos配列を、push時に子に渡し、
子からは、emitで値を親に渡して、親のコンポーネントで配列の追加を行っております。

下記は、そのソースコードとなっております。
page1、page2それぞれ配列を追加できる形になっておりますが、
描画の更新が行われません。またこのほかに方法があればご教授願いたいです。

 ソースコード (試したこと①の内容)

//App.vue

<template>
  <v-ons-navigator
    :page-stack="pageStack"
    @push-page="pushPage"
    @pop-page="popPage"
    @add-todo="addTodo"
  ></v-ons-navigator>
</template>

<script>
import page1 from './Page1'
import page2 from './Page2'

export default {
  data() {
    return {
      pageStack: [],
      todos: [
        {
          text: 'todo one',
        },
        {
          text: 'todo two',
        },
        {
          text: 'todo three',
        }
      ]
    }
  },
  methods: {
    pushPage(page) {
      this.pageStack.push({
        extends: page,
        data : ()=> {
          return {
            todos: this.todos
          }
        }
      })
    },
    popPage() {
      this.pageStack.pop();
    },
    addTodo(val) {
      this.todos.push({text: val})
    }
  },
  created: function(){
    this.pushPage(page1);
  }
}
</script>
// Page1.vue

<template>
  <v-ons-page>
    <custom-toolbar>Page 1</custom-toolbar>
    <v-ons-list-item v-for="(todo) in todos">
      <label>
        {{ todo.text }}
      </label>
    </v-ons-list-item>

    <ons-button @click="nextPage">
      Page2に遷移する
    </ons-button>
    <ons-button @click="change">
      todoを追加する
    </ons-button>
  </v-ons-page>
</template>

<script>
import page2 from './Page2'
export default {
  methods: {
    nextPage() {
      this.$emit('push-page', page2)
    },
    change() {
      this.$emit('add-todo', 'page1の値です')
    }
  },
  props: ['pageStack']
}
</script>
//Page2.vue

<template>
  <v-ons-page>
    <custom-toolbar>Page 2</custom-toolbar>
    <ons-button @click="pop">
      Page1に戻る
    </ons-button>
    <ons-button @click="change">
      todoを追加する
    </ons-button>
  </v-ons-page>
</template>

<script>
export default {
  methods: {
    pop() {
      this.$emit('pop-page')
    },
    change() {
      this.$emit('add-todo', 'page2の値です')
    }
  },
  props: ['pageStack']
}
</script>

 試したこと

①pushの第二引数でextendを使用し、data型で直接渡す方法を試しました。
結果:参照で渡せないことと、親に返すだけではPage1とPage2の同期がとれないことが問題となりました。
追記:子の間でも値を共有していることはconsole.logにて確認できましたが、再描画のみが行われていない状態でした。

②vueの解説でよくあるpropsで行うやりかたも、ons-navigatorが絡むと形がイメージできず、
取り入れることができませんでした。
追記:ons-navigatorでpropsは利用できないとのことでした。代わりのextends。

③Vue.prototype.$hoge というグローバルに近いものも試しましたが、思ったように値変更ができないようで断念しました。

 補足情報

vue : 2.5.16
vue-onsenui : 2.6.1
onsenui : 2.10.3

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 4

checkベストアンサー

+2

VueのコアライブラリのVuex使うのも手です
https://vuex.vuejs.org/ja/

monacaなのでターミナルで
npm install --save vuex
でインストールして

main.js

import Vue from 'vue';
import VueOnsen from 'vue-onsenui';
import Vuex from 'vuex';

import 'onsenui';
// Onsen UI Styling and Icons
require('onsenui/css-components-src/src/onsen-css-components.css');
require('onsenui/css/onsenui.css');

import App from './App';

Vue.use(VueOnsen);
Vue.use(Vuex);

const store = new Vuex.Store({
  state: {
    todos: [
      { id: 1, text: 'aaaa', done: true },
      { id: 2, text: 'bbbb', done: false }
    ]
  },
  getters: {
    doneTodos: state => {
      return state.todos.filter(todo => todo.done)
    }
  },
  mutations: {
    addTodos (state, data) {
      state.todos.push(data)
      console.log(state.todos)
    }
  },
  actions: {
    addTodos (context, data) {
      context.commit('addTodos', data)
    }
  }
})

new Vue({
  el: '#app',
  template: '<app></app>',
  components:{App},
  store
});

App.vue

<template>
  <v-ons-navigator
    :page-stack="pageStack"
    @push-page="pushPage"
    @pop-page="popPage"
  ></v-ons-navigator>
</template>

<script>
import page1 from './Page1'

export default {
  data() {
    return {
      pageStack: [],
    }
  },
  methods: {
    pushPage(page) {
      this.pageStack.push(page)
    },
    popPage() {
      this.pageStack.pop();
    }
  },
  created: function(){
    this.pushPage(page1);
  }
}
</script>

Page1.vue

<template>
  <v-ons-page>
    <custom-toolbar>Page 1</custom-toolbar>
    <ons-button @click="nextPage">
      Page2に遷移する
    </ons-button>
    <div>
      <div v-for="data in todos">
        {{ data.text }}
      </div>
    </div>
  </v-ons-page>
</template>

<script>
import { mapState } from 'vuex'
import page2 from './Page2'
export default {
  methods: {
    nextPage() {
      this.$emit('push-page', page2)
    },
  },
  computed: mapState({
    todos: state => state.todos,
  }),
  props: ['pageStack']
}
</script>

Page2.vue

<template>
  <v-ons-page>
    <custom-toolbar>Page 2</custom-toolbar>
    <ons-button @click="pop">
      Page1に戻る
    </ons-button>
  </v-ons-page>
</template>

<script>
import page2 from './Page2'
export default {
  data() {
    return {
    }
  },
  methods: {
    pop() {
      this.$store.dispatch('addTodos', { id: 3, text: 'cccc', done: true })
      this.$emit('pop-page')
    },
  },
  props: ['pageStack']
}
</script>

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/11/15 00:03

    回答ありがとうございます。
    質問の本文編集とタイミングが被ってしまいすみません。
    vuexについては触れたことがなかったので、少し調べてみます。

    キャンセル

  • 2018/11/15 00:50

    vuexの解説を読むと私が期待していた内容がそこにありました。
    頂いたコードをひとつひとつ実行させていただきましたが、無事動作を確認できました。
    ありがとうございます。vuexの機能を最大限に引きだすにはまだまだ勉強が必要ですが、
    今後の展開としてもvuexは貴重な知識となりました。

    また、現在本文に記載している不具合として、頂いたソースコードから気付きがありました。
    その点については、本文で記述させていただきます。ありがとうございました。

    キャンセル

+2

 状態管理方法について

  1. 単なるオブジェクトへのアクセス
  2. storeパターン
  3. Vuex

私の知る限りVueでの状態管理方法は上記3つ存在します。

3つの特徴を理解して好きなもの(適切なもの)を選べば良いかと思います。

小さなアプリケーションであれば1か2で問題ありません。
大規模アプリケーションの場合は3をおすすめします。

 1. 単なるオブジェクトへのアクセス

以下プログラムの通りです。
一番シンプルですが、オブジェクトの値を直接変更できてしまう為デバッグが困難になる場合があります。

余談ですが、私は個人的なアプリケーションを時間をかけずに開発する場合この方法を利用しています。

公式サイトより引用

this.$root.$data を通じてアクセスすることもできます。
ただ1つの情報源を持つことにはなりましたが、このままだとデバッグは悪夢になるでしょう。
どんなデータでも、アプリケーションのどこからでも痕跡を残すことなく変えることができてしまいます。

 ファイル構成例 

 app.js 

const app = new Vue({
  router,
  el: '#app',
  components: { App },
  template: '<App/>',
  data () {
    return {
      hoge: 'hello world'
    }
  }
})

app.$mount('#app')


 Index.vue 

export default {
  data () {
    return {
    }
  },
  async mounted () {
    console.log(this.$root.hoge)
  }
}

参考
https://jp.vuejs.org/v2/guide/state-management.html#%E3%82%B7%E3%83%B3%E3%83%97%E3%83%AB%E3%81%AA%E7%8A%B6%E6%85%8B%E7%AE%A1%E7%90%86%E3%82%92%E3%82%BC%E3%83%AD%E3%81%8B%E3%82%89%E4%BD%9C%E3%82%8B

 2. storeパターン

1の弱点を補う為に考えられたパターンです。
オブジェクトへ直接アクセスを行わず、メソッドを経由して値を書き換えるという考え方です。
メソッドを経由するためデバッグをログを吐かせればデバッグが簡単になります。

var store = {
  debug: true,
  state: {
    message: 'Hello!'
  },
  setMessageAction (newValue) {
    if (this.debug) console.log('setMessageAction triggered with', newValue)
    this.state.message = newValue
  },
  clearMessageAction () {
    if (this.debug) console.log('clearMessageAction triggered')
    this.state.message = ''
  }
}

参考
https://jp.vuejs.org/v2/guide/state-management.html#%E3%82%B7%E3%83%B3%E3%83%97%E3%83%AB%E3%81%AA%E7%8A%B6%E6%85%8B%E7%AE%A1%E7%90%86%E3%82%92%E3%82%BC%E3%83%AD%E3%81%8B%E3%82%89%E4%BD%9C%E3%82%8B

 3. Vuex

規模が大きくなることが見込まれている場合はVuexを使うべきです。
Vuexを使うと適切な管理ができます。

しかし、小さなアプリケーション開発の場合は非常に冗長だと感じるはずです。

公式サイトより引用

Vuex は、共有状態の管理に役立ちますが、さらに概念やボイラープレートのコストがかかります。これは、短期的生産性と長期的生産性のトレードオフです。
もし、あなたが大規模な SPA を構築することなく、Vuex を導入した場合、冗長で恐ろしいと感じるかもしれません。
そう感じることは全く普通です。あなたのアプリがシンプルであれば、Vuex なしで問題ないでしょう。単純な ストアパターン が必要なだけかもしれません。

参考
https://vuex.vuejs.org/ja/

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/11/20 22:59

    回答ありがとうございます。
    全ての状態管理方法を出していただき大変参考になっております。
    現在はVuexにて、③を使っているものと思いましたが、形だけで見ると非常に②と似ており、
    それ以上でも以下でもないので、実際はVuexの機能を全く引き出せていないようです。

    また、わたしが今作っているアプリに関しては、そこまで大規模になる予定はないので、
    aglkjggg様の教えていただいた、①と同じ方法でアプリを再構築しました。
    非常に管理しやすく重宝しております。デバッグが困難らしいですが、またその際に、
    VuexやStoreでの管理方法への移行を検討したいとおもいます。

    為になる情報助かりました。ありがとうございます。

    キャンセル

+1

専門外言語ですが

アルゴリズムの話だと思うので、参加できるかと思っています。

私の考え

まず、vueを親オブジェクトになっているので、ページ間の内容をかんりする親オブジェクトを作成し、
vueをその親オブジェクトから子オブジェクトとして呼び出し、
ページ出力の際に管理しているデータをページ内に含めるように大改造する。

ページ間移動を管理する親オブジェクトは常に子オブジェクトの帰り値を保持するようにコーディングする。

子オブジェクトが値を返さなかったらページ間移動をキャンセルする。

こんな考え方で実装可能になるのではないでしょうか?

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/11/14 09:34

    回答ありがとうございます。
    >ページ間の内容をかんりする親オブジェクト
    おそらくこれは現在App.vueがその役割を担っています。

    ons-navigatorの作りでは、ページを都度pushして移動しておらず、
    Page1の上にPage2が乗っかる形なので、pushの度に値を渡すという実装に代えられません。

    >子オブジェクトが値を返さなかったらページ間移動をキャンセルする。
    Page2をpopし、親に値を渡しても、すでにPage1は表示されているので、
    Page1に値を渡し直す方法がわからない次第となっております。

    on,emitなどを使って対象コンポーネントを指定する方法を無理やりとれなくはないかと
    少しは考えたのですが、実装としてあまりに遠回りな処理なのではと少し感じております。

    理解が浅く、phpsyoshinsya様の方法で実装できるのやもしれませんが、
    私の実力では現在そこに至りません。回答ありがとうございました!

    キャンセル

  • 2018/11/14 16:27

    なるほどです。ただ、もしこれでやればというのがあるなら、遠回りでも実装に成功するか試してみるのは必要ではないでしょうか。

    私もコードを見てみましたが、帰り値が戻ってきていたとしても、それを管理していそうなコードが見当たりません。
    親オブジェクトとなる新しいオブジェクトがVUEの帰り値をチェックしなければ、今後アプリが大きくなってくれば、どこかの条件で結果セットがプッシュされないということも起こりえるのではないかと。もちろん、単なる練習であればそこまでかんが見る必要はないでしょうが、そういう意識を持っでコーディングに望まれる方が特にコンパイル言語では大切なのではないかと。また、一つ確認を忘れていましたが、なぜページ移動でVUEの帰り値を共有したいと思われたのかが明治されていませんでしたね。私の見落としならすみませんが。

    キャンセル

  • 2018/11/14 16:55

    たしかに、、おっしゃる通りです。
    冗長なコードを実装するより先に、効率的な実装法を求めて逆に遠回りになっていたようです。

    >私もコードを見てみましたが、帰り値が戻ってきていたとしても、それを管理していそうなコードが見当たりません。
    回答の妨げになってしまうかと思い、私が試行錯誤したコードは省いていました。
    本来は親のappVueに値を渡す関数を用意していました。
    本日近しい実装を行い次第、コードを改めてあげさせていただきます。

    >なぜページ移動でVUEの帰り値を共有したいと思われたのかが明治されていません
    本文中になぜ実装したいかを追記しました。

    親身に考えてくださり、ありがとうございます。

    キャンセル

  • 2018/11/14 17:25

    何かしらヒントになれば、いいのかとは思いますが。

    ただ、新たなコードが提示されるとのこと、楽しみにしています。

    環境が許すなら、ゆっくり頑張ってください。

    キャンセル

  • 2018/11/14 17:37

    なるほどです。いま質問内容を再度確認しなおしました。ページ2で作成したToDoリストが戻ってこないということですね。

    それなら、根本的にコードが足りませんね。

    書き方として、ページ1から2に移動する際、
    VUEの子オブジェクトであるページ1を開放しないとページが重なる結果となり、もしこのままで実装に成功してもメモリを圧迫するアプリとなってしまう恐れについて考慮したことはありますでしょうか?

    VUEと最後にプッシュしたページは親子関係にありますが、
    今のならページ1を親オブジェクトとし、
    ページ2を子オブジェクトとし、
    VUEを読み込み必須のクラスに設定
    するのがもっともスマートではないかと。

    いかがでしょうか?

    キャンセル

  • 2018/11/14 17:56

    >メモリを圧迫するアプリとなってしまう恐れについて考慮したことはありますでしょうか?
    OnsenUIのNavigatorを使っているのでその恐れはありません。
    Page1の複数回pushは行っていないので、詳しくは下記を確認してください。
    https://qiita.com/coboco/items/5c33e201907623de5270
    https://ja.onsen.io/v1/reference/ons-navigator.html

    >今のならページ1を親オブジェクトとし、
    OnsenUIのNavigatorでのアニメーションが今回の実装の大前提となりますので、
    その方法は選択できない形となります。

    キャンセル

  • 2018/11/14 18:16

    そういう大前提があるのですね。
    私もQiitaさんのサイト読んでみます。

    キャンセル

  • 2018/11/14 18:24

    私はQiitaさんのご紹介いただいたページから答えを既に導き出しました。

    まずは質問者さんのコード再提示をお待ちしています。

    キャンセル

  • 2018/11/14 23:59

    ありがとうございます。
    ソースコードの編集と、問題点の簡略化を行いました。

    キャンセル

+1

◆◆◆◆◆
◆方法①:push時の参照渡し方法
再描画がされない件については、
対象の値が<v-ons-page>直下に書いてあることが原因?だった。

<div>
  <v-ons-list-item v-for="(todo) in todos">
    <label>
      {{ todo.text }}
    </label>
  </v-ons-list-item>
</div>


このように<div>で囲むことで無事に更新を確認できた。
rururu3様の、コードを自分で書いてある途中、値が更新されない件に気付き、
そのまま流用した際に、再描画されたことから発見につながった。
詳細についてはまた詳しくリファレンスを読んでいきたいと思います。

◆◆◆◆◆
◆方法②:vuexでの管理
rururu3様のコードを参照

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

同じタグがついた質問を見る

  • JavaScript

    17608questions

    JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

  • Monaca

    1013questions

    「Monaca」はiOS、Android、Windows向けのアプリ開発に対応した、Cordovaベースのモバイルアプリ開発プラットフォームです。HTML5、JavaScriptといったWeb標準技術を用いてモバイルアプリ開発を行うことができます。

  • Vue.js

    852questions

    Vue.jsは、Webアプリケーションのインターフェースを構築するためのオープンソースJavaScriptフレームワークです。

  • Onsen UI

    334questions

    HTML5で記述されたモバイルアプリの高速化、およびネイティブアプリライクなUIが作れるフレームワーク。 様々なJavaScriptフレームワークと併せて使用することができます。スマートフォン向けアプリ、Webサイトに必要なアニメーション、UI/UXを実装することが可能になります。