Techvenience

Technology × Convenience - Vue / React / Next / Nuxt / ChatGPTなどのIT技術がもたらす便利さをお伝えします。最近はChatGPTなどのAI技術を使ってブログを書いています。

【Vue.js】単一ファイルコンポーネントを使わないVue.jsで、画面間のデータ受け渡し。【ネストルート】

【Vue.js】単一ファイルコンポーネントを使わないVue.jsで、画面間のデータ受け渡し。【ネストルート】

f:id:duo-taro100:20160218004611p:plain

前提

単一ファイルコンポーネントを使わないVue.jsでの実装。
vue-routerを使ってルーティング。
vue.jsとvue-routerはそれぞれcdnを使用。

<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>

やりたいこと

あるルーティングにネストされたルーティングが複数あったとして、その画面間でデータのやり取りをしたい。
例えば、ルートパス「/money」に「/」(/money/)と「/future」(/money/future)というパスがネストされているとする。

お金ルート画面
  お金画面
  未来画面

①お金画面で、「aaa」と入力する。
②未来画面に遷移
③未来画面で、「bbb」と入力する。
④未来画面でボタンをクリックすると「aaa / bbb」と表示される。

つまり、お金画面で入力した値を未来画面でも使いたい。

ディレクトリ構成

こんな感じ。(cssなどは省略)

index.html

js
  component
    moneyComponent.js
  router
    router.js

ネストルーティングの定義

■route.js

// コンポーネント
const TopRoot = { template: '<toproot></toproot>' }
const MoneyRoot = { template: '<moneyroot></moneyroot>' }
const Money = { template: '<money></money>' }
const Future = { template: '<future></future>' }

const routes = [
	{ path: '/', component: TopRoot},
  	{ path: '/money', component: MoneyRoot,
    	children:[
      		{path: '', component: Money},
      		{path: 'future', component: Future},
    	]
  	}

]

const router = new VueRouter({
  routes
})
childrenオプションを使ってネスト。

ネストルートがない場合は以下のように書く。

  { path: '/money', component: MoneyRoot}

注意することはchildrenのpathには'/'が不要であるということ。
今回の例で言えば、「/money」もしくは「/money/」にアクセスしたときMoneyコンポーネントの中身が表示される。

	{path: '', component: Money},

「/money/future」にアクセスしたときFutureコンポーネントの中身が表示される。

	{path: 'future', component: Future},

index.html

<!DOCTYPE html>
<html lang="ja">
<head>
	<meta charset="utf-8">
	<script src="https://unpkg.com/vue/dist/vue.js"></script>
	<script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
</head>
<body>
	<div id="app">
		<div class="container">
			<div class="construct">
				<div class="main">
					<!-- routerで定義したコンポーネントを表示 -->
					<router-view></router-view>
				</div>
			</div><!-- .construct -->
		</div><!-- .container -->
	</div>
</body>
</html>
<script src="js/components/moneyComponent.js"></script>
<script src="js/route/router.js"></script>
<script>
	// Vueインスタンス作成して、router注入
	var app = new Vue({
		el : "#app",
		router
	})
</script>

route.jsで定義したコンポーネントの中身がでに表示される。
ただし、ここに表示されるのはpathが「/」か「/money」の場合のみ。
「/money/」と「/money/future」についてはMoneyRootコンポーネントに定義するもう一つのに表示される。
これは後ほど。

コンポーネント定義

/*********************************
          main
*********************************/
var store = {
  debug : true,
  state : {
    money : '',
    test : ''
  },
  setMoneyAction (newValue) {
    if (this.debug) console.log('setMoneyAction triggered with', newValue)
    this.state.money = newValue
  },
  clearMoneyAction () {
    if (this.debug) console.log('clearMoneyAction triggered')
    this.state.money = ''
  },
  setTestAction (newValue) {
    if (this.debug) console.log('setTestAction triggered with', newValue)
    this.state.test = newValue
  },
  clearTestAction () {
    if (this.debug) console.log('clearTestAction triggered')
    this.state.test = ''
  }
}

// moneyRootComponent
Vue.component('moneyroot', {

    template : 
        '<div class="contents">'+
          '<div class="money-root-parent">'+
            '<div class="money-root-">'+
              '<p>お金</p>'+
              '<router-view></router-view>'+
            '</div><!-- .money-root- -->'+
          '</div><!-- .money-root--parent -->'+
        '</div><!-- .contents -->',
})

/*********************************
              sub
*********************************/
// moneyComponent
Vue.component('money', {

    template : 
        '<div class="contents">'+
          '<div class="money-parent">'+
            '<div class="money">'+
              '<p>お金</p>'+
              '<input type="text" v-model="moneytest">'+
            '</div><!-- .money -->'+
          '</div><!-- .money-parent -->'+
        '</div><!-- .contents -->',
    data: function () {
        return {
          moneytest : ''
        }
    },
    updated : function(){
      store.setMoneyAction(this.moneytest)
    }
})

// futureComponent
Vue.component('future', {

    template : 
        '<div class="contents">'+
          '<div class="future-parent">'+
            '<div class="future">'+
              '<p>未来</p>'+
              '<input type="text" v-model="moneytest2">'+
              '<button @click="output()">ボタン</button>'+
              '<p v-model="result"></p>'+
            '</div><!-- .future -->'+
          '</div><!-- .future-parent -->'+
        '</div><!-- .contents -->',
    data: function () {
        return {
          moneytest2 : '',
          result: ''
        }
    },
    methods: {
      output : function(){
        this.result = store.state.money + ' / ' + store.state.test;
      }
    },
    updated : function(){
      store.setTestAction(this.moneytest2)
    }
})

storeが今回の問題の救世主。
https://jp.vuejs.org/v2/guide/state-management.html
ここに記載がある。

var store = {
  debug : true,
  state : {
    money : '',
    test : ''
  },
  setMoneyAction (newValue) {
    if (this.debug) console.log('setMoneyAction triggered with', newValue)
    this.state.money = newValue
  },
  clearMoneyAction () {
    if (this.debug) console.log('clearMoneyAction triggered')
    this.state.money = ''
  },
  setTestAction (newValue) {
    if (this.debug) console.log('setTestAction triggered with', newValue)
    this.state.test = newValue
  },
  clearTestAction () {
    if (this.debug) console.log('clearTestAction triggered')
    this.state.test = ''
  }
}

これを最初に定義しておいて、入力した値をここに詰めて持ち回す感じ。
使い方は以下参照。

moneyroot

/moneyのときにアクセスするコンポーネント。

// moneyRootComponent
Vue.component('moneyroot', {

    template : 
        '<div class="contents">'+
          '<div class="money-root-parent">'+
            '<div class="money-root-">'+
              '<p>お金ルート</p>'+
              '<!-- ここに未来画面、今画面が表示される -->'+
              '<router-view></router-view>'+
            '</div><!-- .money-root- -->'+
          '</div><!-- .money-root--parent -->'+
        '</div><!-- .contents -->',

})

ここにはさっき記載した、2つ目のがここで出てくる。
デフォルト状態では「/money/」なので、 Moneyコンポーネントが表示される。

moneyComponent

/money/のときにアクセスするコンポーネント。

// moneyComponent
Vue.component('money', {

    template : 
        '<div class="contents">'+
          '<div class="money-parent">'+
            '<div class="money">'+
              '<p>デフォルト</p>'+
              '<input type="text" v-model="moneytest">'+
            '</div><!-- .money -->'+
          '</div><!-- .money-parent -->'+
        '</div><!-- .contents -->',
    data: function () {
        return {
          moneytest : ''
        }
    },
    updated : function(){
      store.setMoneyAction(this.moneytest)
    }
})

「v-model="moneytest"」を用意して、その値が変化したらstoreに入力値を設定する。

updated : function(){
  store.setMoneyAction(this.moneytest)
}
futureComponent

/money/futureのときにアクセスするコンポーネント。

// futureComponent
Vue.component('future', {

    template : 
        '<div class="contents">'+
          '<div class="future-parent">'+
            '<div class="future">'+
              '<p>未来</p>'+
              '<input type="text" v-model="moneytest2">'+
              '<button @click="output()">ボタン</button>'+
              '<p v-model="result"></p>'+
            '</div><!-- .future -->'+
          '</div><!-- .future-parent -->'+
        '</div><!-- .contents -->',
    data: function () {
        return {
          moneytest2 : '',
          result: ''
        }
    },
    methods: {
      output : function(){
        this.result = store.state.money + ' / ' + store.state.test;
      }
    },
    updated : function(){
      store.setTestAction(this.moneytest2)
    }
})


「v-model="moneytest2"」を用意して、その値が変化したらstoreに入力値を設定する。

    updated : function(){
      store.setTestAction(this.moneytest2)
    }

ボタンを押したときに、storeに詰めた「」と「」の入力値を取得して、結果に詰めて表示するメソッド。

<button @click="output()">ボタン</button>
methods: {
  output : function(){
    this.result = store.state.money + ' / ' + store.state.test;
  }
}

これでできる。
https://jp.vuejs.org/v2/guide/state-management.htmlを読み進めていくと、「vuex」を使ってみるともっといいよ!
って書いてあるので、これがベストアンサーではないかも?