Reputation: 1
I have created this Vue.js router example to wrap my mind around how routing works. I intentionally load it everything from CDN so that whoever looks at this, gets to begin learning immediately instead of having to learn how to import dependencies, etc.
My question is, where do I place the methods that are going to fetch data from a JSON API based on the route parameter of that route? In the App, or in the Component?
Forgive if my question seems naive. I think I'm almost there.
Run Code Snippet then click Full Page to better view.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Simple Vue.js Router Example</title>
<!-- VUE JS v2.6.1 -->
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<!-- VUE ROUTER JS v3.1.3 -->
<script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
<!-- BOOTSTRAP CSS v4.3.1 -->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
<!-- GOOGLE FONT CSS - Roboto Mono -->
<link href="https://fonts.googleapis.com/css?family=Roboto+Mono:100,300,400,500,700&display=swap" rel="stylesheet">
<!-- GOOGLE FONT CSS - Material Icons -->
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
<style type="text/css">
body {
font-family: 'Roboto Mono', monospace;
font-weight: 400;
font-size: 1rem;
background-color: #e0e0e0;
}
.active {
color: #f44336;
}
</style>
</head>
<body>
<!-- VUE APP - PARENT CONTAINER -->
<div id="app" class="container">
<!-- HEADER CONTAINER -->
<header>
<hr>
<h1>Header</h1>
<p>Static header text</p>
<ul>
<li>
<router-link to="/">/ </router-link>
</li>
<li>
<router-link to="/users">/users</router-link>
</li>
<li>
<router-link to="/users/123">/users/123</router-link>
</li>
<li>
<router-link to="/posts">/posts</router-link>
</li>
<li>
<router-link to="/posts/456">/posts/456</router-link>
</li>
<li>
<router-link to="/unsaved-changes">/unsaved-changes</router-link>
</li>
<li>
<router-link to="/unknown-route/789">/unknown-route/789</router-link>
<br>
<small>*forwards to route /404</small>
</li>
</ul>
</header>
<!-- MAIN CONTAINER -->
<main>
<hr>
<h1>Main</h1>
<p>Static main text</p>
<router-view name="routerView0"></router-view>
<router-view name="routerView1"></router-view>
<router-view name="routerView2"></router-view>
<router-view name="routerView3"></router-view>
<router-view name="routerView4"></router-view>
<router-view name="routerView5"></router-view>
<router-view name="routerView6"></router-view>
</main>
<!-- FOOTER CONTAINER -->
<footer>
<hr>
<h1>Footer</h1>
<p>Static footer text</p>
</footer>
</div>
<!-- JAVA SCRIPT -->
<script type="text/javascript">
// DISABLE
Vue.config.productionTip = false;
// DISABLE
Vue.config.devtools = false;
// COMPONENT 0
const Component0 = {
template:
`
<div style="background-color: #bcaaa4;">
<strong>Component 0</strong>
<br>
success: route /
<br>
result: component rendered.
</div>
`
}
// COMPONENT 1
const Component1 = {
template:
`
<div style="background-color: #80deea;">
<strong>Component 1</strong>
<br>
success: route /users
<br>
result: component rendered.
</div>
`
}
// COMPONENT 2
const Component2 = {
template:
`
<div style="background-color: #80deea;">
<strong>Component 2</strong>
<br>
success: route /users/{{ $route.params.id }}
<br>
result: component rendered.
</div>
`
}
// COMPONENT 3
const Component3 = {
template:
`
<div style="background-color: #b39ddb;">
<strong>Component 3</strong>
<br>
success: route /posts
<br>
result: component rendered.
</div>
`
}
// COMPONENT 4
const Component4 = {
template:
`
<div style="background-color: #b39ddb;">
<strong>Component 4</strong>
<br>
success: route /posts/{{ $route.params.id }}
<br>
result: component rendered.
</div>
`
}
// COMPONENT 5
const Component5 = {
template:
`
<div style="background-color: #ffe082;">
<strong>Component 5</strong>
<br>
success: route /unsaved-changes
<br>
result: component rendered.
<br>
<small><strong>*If you leave this route,<br> all text typed in below will be lost.</strong></small>
<br>
<input type="text">
</div>
`,
// IN COMPOMENT ONLY...
beforeRouteLeave (to, from, next) {
const answer = window.confirm('Are you sure you want to leave this route? There are unsaved changes!')
if (answer) {
next()
} else {
next(false)
}
}
}
// COMPONENT 6
const Component6 = {
template:
`
<div style="background-color: #ef9a9a;">
<strong>Component 6</strong>
<br>
error: unknown route.
<br>
action: forwarded to route /404.
<br>
result: component rendered.
</div>
`
}
// IN THIS ROUTE I WILL RENDER THESE COMPONENTS..
const router = new VueRouter({
mode: 'hash',
linkExactActiveClass: "active",
routes: [
// ROUTE 0
{ path: '/',
// COMPONENT(S) TO RENDER IN ORDER
components: {
// ONE OR MORE...
routerView0: Component0,
}
}, // END ROUTE 0
// ROUTE 1
{ path: '/users',
// COMPONENT(S) TO RENDER
components: {
// ONE OR MORE...
routerView1: Component1,
}
}, // END ROUTE 1
// ROUTE 1.1
{ path: '/users/:id',
// COMPONENT(S) TO RENDER
components: {
// ONE OR MORE...
routerView2: Component2,
},
// REPORT WHEN THIS ROUTE IS VISITED
beforeEnter: (to, from, next) => {
// ...
console.warn('ROUTE CHANGE')
console.log('ROUTE', 'FROM:', from.path, 'TO:', to.path);
next();
}
}, // END ROUTE 1.1
// ROUTE 2
{
path: '/posts',
components: {
// ONE OR MORE...
routerView3: Component3,
}
}, // END ROUTE 2
// ROUTE 2.1
{
path: '/posts/:id',
components: {
// ONE OR MORE...
routerView4: Component4,
}, // END ROUTE 2.1
// REPORT WHEN THIS ROUTE IS VISITED
beforeEnter: (to, from, next) => {
// ...
console.warn('ROUTE CHANGE')
console.log('ROUTE', 'FROM:', from.path, 'TO:', to.path);
next();
}
},
// ROUTE UNSAVED CHANGES
{
path: '/unsaved-changes',
components: {
// ONE OR MORE...
routerView5: Component5,
}
}, // END ROUTE UNSAVED CHANGES
// REDIRECT!
{
path: '*', redirect: '/404',
// TRAP ANY UNDEFINED ROUTE AND...
// FORWARD IT TO /404 ROUTE
},
// ROUTE UNDEFINED - CUSTOM PAGE
{
path: '/404',
components: {
// ONE OR MORE...
routerView6: Component6,
}
}, // END ROUTE UNDEFINED
]
});
// WATCH EVERY ROUTE THAT IS VISITED
/*
router.beforeEach((to, from, next) => {
// ...
console.info('Global Route Watcher')
console.log('ROUTE', 'FROM:', from.path, 'TO:', to.path);
next();
});
*/
const App = new Vue({
el: '#app',
router,
data: {
},
})
</script>
</body>
</html>
Upvotes: 0
Views: 55
Reputation: 81
I personally do the fetch inside the components, you can fetch the data on the created or beforeCreate hook if you want the component to render before fetching the data and add a loading animation, alternatively you could use the beforeRouteEnter or beforeRouteUpdate hooks on the component.
My logic behind this is the following, if you fetch the data from the component they would be easier to test by just passing them a new route param and you avoid doing some complex logic on the App component because it wont have to track which route is selected and act accordingly.
take a look on solid and Single responsibility principle
Upvotes: 1