Edited at

Vue-routerでコンポーネント側のpropsの値に応じてURLを変更したい

More than 3 years have passed since last update.


やりたいこと


  • Vue-routerのルーティングで使われるComponent側のpropsの変更でもURLを変更したい


課題



  • <router-view>v-bind:paramName.sync="$route.params.paramName"と渡してもURLは変更されない。値は変わっているが検知されない


    • ただし、URLを変更して$route.params.paramNameが変更された場合は検知され、コンポーネントのpropsも変更される



  • 仮に上記方法で出来たとしても、ルーティングが増えればそれだけコンポーネントも増え、渡す値も増えるのでv-bindでやると破綻が見えている


てきとうなSPAアプリ

var News = Vue.extend({

props:{
newsId:{
type:Number,
default:1
}
}
});
var Mail = Vue.extend({
props:{
mailId:{
type:Number,
default:1
}
}
});
var Todo = Vue.extend({
props:{
todoId:{
type:Number,
default:1
}
}
});
var router = new VueRouter();
router.map({
'/News/:news_id':News,
'/Mail/:mail_id':Mail,
'/Todo/:todo_id':Todo
});
/*
この時v-bindでpropsと結びつけようとすると、View側の<router-view>に
<router-view :news_id="$route.params.news_id" :mail_id="$route.params.mail_id" :todo_id="$route.params.todo_id"></router-view>
とかやらなきゃならず、コンポーネントが増えれば増えるほど、各々のコンポーネントから見れば要らないものを結びつけようとする
*/



  • と言ってComponent内で$route.paramsを使うと密結合になってしまう


とりあえずの方法


  • router.mapでルーティングにコンポーネントを割り当てる際に予め定義したコンポーネントをextendしてwatchとcreatedで値をsyncさせる


サンプル

http://jsdo.it/hadakadenkyu/uizq

// URLパラメータはStringなので数値型に変換

Vue.filter('int',function(val){return parseInt(val);});
var App = Vue.extend();
var router = new VueRouter();
var Component = Vue.extend({
template:"<p>Current route params: {{$route.params | json}}</p><p>pageNum: <input v-model='pageNum' type='number' number></p>",
props:{
pageNum:{ // 1. この値に応じてURLを変えたい
type:Number,
default:1,
twoWay:false // trueにしても意味がない。$route.paramsはdefinePropertyでプロパティを設定していないから見張れない
}
}
});
// 2. 疎結合ならrouterの存在はComponentは知らない筈なので・・・
router.map({
'/page/:pagenumber': {
component: Component.extend({ // 3. routerの中でComponentを割り当てる時にwatchでpageNumを見張るよう設定する
watch:{
'pageNum': function (val, oldVal) {
router.go('/page/'+val);
},
'$route.params.pagenumber':function(val,oldVal){
this.pageNum = parseInt(val);
}
},
created:function(){ // 4. 初期値はv-bindでなくこっちで入れないとrouter-viewタグに全部書く羽目になる
this.pageNum = parseInt(this.$route.params.pagenumber);
}
}),
// 5. 例えばこう書ければいいのに
/*
bind:{'pagenumber':{
name:'pageNum',
sync:true // changing pageNum invokes router.go
}
}
*/

}
});
router.start(App, '#app',function(){
router.go('/page/5');
});


疑問点


  • もっとシンプルに書けないものか

  • イベントドリブンにしてComponentから$dispatchしてAppでrouter.goするのとどっちが良かろうか