match vue js routes with query string in them? - vue.js

I was reading here:
https://router.vuejs.org/en/essentials/dynamic-matching.html
but it doesn't seem to let me match say:
/mycoolapp?someid=123
yes, I could do
/mycoolapp/123
but that is not the question
The question is: can I match routes dynamically with get query parameters?

You can access the query parameters, in your example someid via $route.query.someid. From $route docs:
$route.query
type: Object
An object that contains key/value pairs of the query string. For
example, for a path /foo?user=1, we get $route.query.user == 1. If
there is no query the value will be an empty object.
Demo below:
const UserList = {
template: '#users',
data() { return { users: [] } },
created() {
axios.get('https://jsonplaceholder.typicode.com/users').then(r => this.users = r.data);
}
};
const User = {
props: ['uid', 'user'],
template: '#user'
}
var router = new VueRouter({
routes: [{
path: '/',
component: UserList
},
{
path: '/user/:uid',
component: User,
name: 'user',
props: true
}]
});
var app = new Vue({
el: '#app',
router: router,
template: '<router-view></router-view>'
});
<script src="https://unpkg.com/vue"></script>
<script src="https://unpkg.com/vue-router"></script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<template id="users" functional>
<div>
<ul>
<li v-for="user in users">
<div>
{{ user.name }} -
<router-link :to="{name: 'user', params: {uid: user.id, user: user}, query: {field: 'email'}}">(email)</router-link> -
<router-link :to="{name: 'user', params: {uid: user.id, user: user}, query: {field: 'website'}}">(website)</router-link>
</div>
</li>
</ul>
</div>
</template>
<template id="user" functional>
<div class="row">
<b>{{ user.name }}</b><br>
{{ $route.query.field }}: {{ user[$route.query.field] }}<br>
<router-link :to="{path: '/'}">back</router-link>
</div>
</template>
<div id="app">
<p>{{ message }}</p>
</div>
On the other hand, if you want to redirect to a specific component depending on the query parameters, you can use navigation guards. For instance, beforeEach:
router.beforeEach((to, from, next) => {
if (to.query.field === 'email') {
next({path: '/email'}); // some hypothetical path
} else {
next();
}
});

vue-router uses this to handle paths
can help you find a solution
there are only a few mentions of query in the documentation
When dealing with query strings, escape the question mark (?) so it doesn't mark the parameter as optional. Handling unordered data is outside the scope of this library.
const regexp = pathToRegexp("/search/:tableName\\?useIndex=true&term=amazing");
regexp.exec("/search/people?useIndex=true&term=amazing");
//=> [ '/search/people?useIndex=true&term=amazing', 'people', index: 0, input: '/search/people?useIndex=true&term=amazing', groups: undefined ]
// This library does not handle query strings in different orders
regexp.exec("/search/people?term=amazing&useIndex=true");
//=> null
but I still couldn't make a route with a query string

Related

how to use router-link custom navigation instead of `to` prop

<router-link to="/" tag="a" :title="title">
<span class="icon icon-home i-large" />
<span class="class-2">Name</span>
</router-link>
This is what I have...
As you can see , I am using to prop. But I don't want to use it. Whenever someone clicks on this, i want to execute a function and use programatic navigation.
How is this possible? the structure and htmls that I have shouldn't change and it still should work as expected.
Something like this you mean?
<a #click="functionThatExecutesMyCode">
<span class="icon icon-home i-large" />
<span class="class-2">Name</span>
</a>
functionThatExecutesMyCode() {
this.$router.push({ to: 'newRoute'})
}
Else you should use the navigation guards: https://router.vuejs.org/guide/advanced/navigation-guards.html
I don't think it's possible to change :to props to add a function, because it is predefined as only accept a string in its docs. https://router.vuejs.org/api/#to
However, I suggest another way to implement what you want, you can add the logic in beforeEach hook. For example
router.beforeEach( (to, from, next) => {
// implement your logic here, for example if no internet, go to error page
if (!navigator.onLine && to.name !== "NetworkError") {
next({ name: "NetworkError" });
return false;
}
next();
}
For more info, look at their docs: https://router.vuejs.org/api/#router-beforeeach
It depends on what you exactly want.
There are easier solutions, like:
const Root = {
template: '<div>Root</div>'
}
const Route1 = {
template: '<div>Route1</div>'
}
const routes = [{
path: '/',
name: 'root',
component: Root
},
{
path: '/1',
name: 'route1',
component: Route1
}
]
const router = new VueRouter({
routes // short for `routes: routes`
})
new Vue({
el: "#app",
router,
data: {
selected: 'root',
options: ['root', 'route1']
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
<div id="app">
<select v-model="selected">
<option v-for="(option, i) in options">
{{ option }}
</option>
</select>
<router-view></router-view>
<router-link :to="{ name: selected }">{{ selected }}</router-link>
</div>
In this solution I used a named route, and the "name" can be changed by the select input.
The next solution is to incorporate a function:
const Root = {
template: '<div>Root</div>'
}
const Route1 = {
template: '<div>Route1</div>'
}
const routes = [{
path: '/',
name: 'root',
component: Root
},
{
path: '/1',
name: 'route1',
component: Route1
}
]
const router = new VueRouter({
routes // short for `routes: routes`
})
new Vue({
el: "#app",
router,
data: {
selected: 'root',
options: ['root', 'route1']
},
methods: {
changeRouteName() {
return this.selected
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
<div id="app">
<select v-model="selected">
<option v-for="(option, i) in options">
{{ option }}
</option>
</select>
<router-view></router-view>
<router-link :to="{ name: changeRouteName() }">{{ selected }}</router-link>
</div>
By adding a method to resolve the name of the component, it's possible to build ANY logic on the to of the <router-link. You could also use path instead of name of course - I just like named components.
It does not remove the click event from the to prop, but you can do your logic before returning the name it's "waiting" for.

What key to use for v-for when elements have no id?

When using v-for, it is strongly recommended to include a key. Furthermore, as explained here, using the array index as your key doesn't really help.
If your elements have an id property, then that's great - you could just use that as your key. But what about when your elements don't have an id property? What should you use as your key in that scenario?
Better solution
A better solution would be to use an external library to generate a hashcode from the value of your object and use that as an id. i.e. object-hash
An example using object-hash
const hash = objectHash; // this should be the import i.e. require('object-hash');
new Vue({
el: '#app',
template: `
<div>
<p v-for="item in itemsWithHash" :key="item.key">
{{item.name}} {{item.lastname}}<br>key: {{ item.key }}
</p>
</div>
`,
data: () => ({
items: [
{ name: 'john', lastname: 'doe' },
{ name: 'bob', lastname: 'smith' },
{ name: 'alice', lastname: 'james' }
]
}),
computed: {
itemsWithHash() {
return this.items.map(i => ({ ...i, key: hash(i) }));
}
}
});
<script src="https://cdn.jsdelivr.net/npm/object-hash#1.3.1/dist/object_hash.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app"></div>
OK Solution
You can use the index of the iteration as a key, but keep in mind that this will not trigger changes on the view if you update the item by index. (Vue uses the key to detect changes, so it wont re-render the template if the key is not updated)
<div v-for="(item, i) in items" :key="i">
// some content.
</div>
Note the example below where mutating the items directly by index does not update the view but prints the change to the console:
new Vue({
el: '#app',
template: `
<div>
<p v-for="(item, index) in items" :key="index">
{{item.name}} {{item.lastname}}<br>index: {{ index }}
<button #click="changeForMary(index)"> Replace with Mary Smith</button>
</p>
</div>
`,
data: () => ({
items: [
{ name: 'john', lastname: 'doe' },
{ name: 'bob', lastname: 'smith' },
{ name: 'alice', lastname: 'james' }
]
}),
methods: {
changeForMary(index){
this.items[index] = { name: 'mary', lastname: 'smith' };
console.log(`index ${index} changed to ${JSON.stringify(this.items[index], null, '\t')}`);
}
}
});
<script src="https://cdn.jsdelivr.net/npm/object-hash#1.3.1/dist/object_hash.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app"></div>

Vue router dynamic link and children reload page - not load correctly component

I add to my routes file path with children:
path: '/warehouse/:id',
name: 'ShowWarehouse',
component: ShowWarehouse,
children: [{
path: 'edit',
name: 'EditWarehouse',
component: EditWarehouse
}
]
Now in component ShowWarehouse I have:
<div v-if="!changeEdit">
<div v-if="warehouseData">
<div>Name: {{ warehouseData.warehouse.name }}</div>
<div>
<router-link
:to="{ name: 'EditWarehouse', params: {id: warehouseData.warehouse.id }}"
>Edit</router-link>
</div>
</div>
</div>
<router-view v-else></router-view>
When the user click edit button I need load component EditWarehouse, but component ShowWarehouse must be disappear, and if user back (without /edit) disappear componet EditWarehouse and load component ShowWarehouse. I write method in watch:
watch: {
$route() {
if (this.$route.path == '/warehouse/' + id_get_from_API + '/edit') {
this.changeEdit = true;
} else {
this.changeEdit = false;
}
}
},
The problem is when the user is at mydomain.com/warehouse/23/edit and click reload page (F5), then Vue loads component ShowWarehouse instead of loading EditWarehouse.
I using mode: 'history'.
Problem:
From the Vue.JS website: "Vue does provide a more generic way to observe and react to data changes on a Vue instance: watch properties." When you refresh the page the watch() method will not be executed because it is a new Vue instance and no data has changed on the Vue instance yet. You should probably use a different pattern to determine which component to show. (https://v2.vuejs.org/v2/guide/computed.html#Computed-vs-Watched-Property)
Solution:
I suggest making the EditWarehouse a sibling route to ShowWarehouse, and make EditWarehouse its own component (you already have this). Your router-link in the ShowWarehouse component can stay the same.
Code Snippet:
const ShowWarehouse = {
template: `<div><h1>ShowWarehouse</h1> <div v-if="warehouseData">
<div>Name: {{ warehouseData.warehouse.name }}</div>
<div>ID: {{ $route.params.id }}</div>
<div>
<router-link :to="{ name: 'EditWarehouse'}">Edit</router-link>
</div>
</div></div>`,
computed: {
warehouseData: function() {
let data;
let id = this.$route.params.id;
if (id) {
data = {
warehouse: {
name: 'Some Warehouse Name',
id: id
}
}
}
return data;
}
}
};
const EditWarehouse = {
template: "<h1>EditWarehouse [{{ $route.params.id }}]</h1>"
}
const router = new VueRouter({
routes: [{
path: '/warehouse/:id',
name: 'ShowWarehouse',
component: ShowWarehouse
},
{
path: '/warehouse/:id/edit',
name: 'EditWarehouse',
component: EditWarehouse
}
]
});
new Vue({
el: '#app',
router
});
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
<div id="app">
<p>
<router-link :to="{ name: 'ShowWarehouse', params: { id: 123 }}">Go to Warehouse 123</router-link>
</p>
<router-view/>
</div>
Here is a jsfiddle with the same code:
https://jsfiddle.net/austinwasinger/oruswb3a/39/

Vuejs does not render data from ajax

I use jQuery ajax to request api and get the respone, but the data cannot be rendered. Does someone know how to fix this problem?
var app = new Vue({
//router,
data: {
people: null
},
created: function() {
$.ajax({
url: 'test.php',
}).done(function(res){
console.log(res);
this.people = JSON.parse(res);
//console.log('124234234523');
//console.log(this.people.name);
});
}
}).$mount('#app');
<div id="app">
<ol>
<li v-for="person in people">
{{ person.name }}
</li>
</ol>
</div>
Code use vue router:
While I use Vue router, even I use arrow function. The template cannot be render by Vue. Whether do I misuse Vue router?
// Listing people component
var PeopleListing = Vue.extend({
template: '#people-listing-template',
data: function() {
return {
people: this.$parent.people
}
}
});
// Create the router
var router = new VueRouter({
mode: 'hash',
base: window.location.href,
routes: [
{path: '#/', component: PeopleListing},
{name: 'person', path: '/:id', component: PersonDetail }
]
});
var app = new Vue({
router,
data: {
people: null
},
created: function() {
$.ajax({
url: 'test.php',
}).done((res) =>{
//console.log(res);
this.people = JSON.parse(res);
//console.log('124234234523');
//console.log(this.people.name);
});
}
}).$mount('#app');
<div id="app">
<router-view class="view"></router-view>
</div>
<template id="people-listing-template">
<ul>
<li v-for="person in people">
{{ person.name }}
<router-link :to="{ name: 'person', params: { id: person.guid }}">View Details</router-link>
</li>
</ul>
</template>
Scope of this is creating issue, use arrow function like following:
var app = new Vue({
//router,
data: {
people: null
},
created: function() {
$.ajax({
url: 'test.php',
}).done((res) => {
console.log(res);
this.people = JSON.parse(res);
//console.log('124234234523');
//console.log(this.people.name);
});
}
}).$mount('#app');
You can get more details about this issue here.

Passing route params in vue router v-link

I have this anchor in Parent.vue:
<a v-link="{ path: '/somepath/somesubpath', query: { messageId: 999}}"> Here </a>
or also this
<a v-link="{ path: '/somepath/somesubpath', params: { messageId: 999}}"> Here </a>
My Child.vue is like this:
<template>
{{messageId}}
// other html..
</template>
<script>
export default {
props: ['messageId']
}
</script>
In Child.vue I can not see the value of {{messageId}} being rendered. This means that the value is not getting passed.
I am using vue router 0.7.5 and the docs here do not specify how to specify params in v-link (without named routes).
So whats the proper way to pass in params using vue router with v-link?
Route params are available in the this.$route.params property. They do not need to be passed as a component property.
In your case you can access the messageId param in your template like so:
{{ $route.params.messageId }}
However, you also need to make sure that the template for your Child.vue component has a single root element and not just text. Otherwise, the component will not render.
So, you'd at least need something like this:
<template>
<div>
{{ $route.params.messageId }}
</div>
</template>
Here's a simple example:
Vue.component('child', {
template: `
<div>
message id: {{ $route.params.messageID }}
</div>
`
});
const Home = {
template: '<div>Home</div>'
};
const Message = {
template: `
<div>
<span>Message View</span>
<child></child>
</div>
`
};
const router = new VueRouter({
routes: [
{ path: '/', component: Home, name: 'home' },
{ path: '/message', component: Message, name: 'message' }
]
})
new Vue({
router,
el: '#app'
})
<script src="https://npmcdn.com/vue/dist/vue.js"></script>
<script src="https://npmcdn.com/vue-router/dist/vue-router.js"></script>
<div id="app">
<router-link :to="{ name: 'home' }">home</router-link>
<router-link :to="{ name: 'message', params: { messageID: 123 } }">message view</router-link>
<router-view></router-view>
</div>