Commit 01d04adc authored by Evan You's avatar Evan You

handle view param changes and user not found

parent 8255a01b
......@@ -2,15 +2,17 @@ import Vue from 'vue'
import 'es6-promise/auto'
import { createApp } from './app'
// a global mixin to invoke fetchData on the client
// a global mixin that calls `asyncData` when a route component's params change
Vue.mixin({
beforeMount () {
beforeRouteUpdate (to, from, next) {
const { asyncData } = this.$options
if (asyncData) {
this.dataPromise = asyncData(
this.$store,
this.$route
)
asyncData({
store: this.$store,
route: to
}).then(next)
} else {
next()
}
}
})
......@@ -26,6 +28,17 @@ if (window.__INITIAL_STATE__) {
// wait until router has resolved all async before hooks
// and async components...
router.onReady(() => {
// add router hook for handling asyncData
// doing it after initial route is resolved so that we don't double-fetch
// the data that we already have.
router.beforeResolve((to, from, next) => {
Promise.all(router.getMatchedComponents(to).map(c => {
if (c.asyncData) {
return c.asyncData({ store, route: to })
}
})).then(next)
})
// actually mount to DOM
app.$mount('#app')
})
......
......@@ -27,11 +27,11 @@ export default context => {
// which is resolved when the action is complete and store state has been
// updated.
Promise.all(matchedComponents.map(component => {
return component.asyncData && component.asyncData(
return component.asyncData && component.asyncData({
store,
router.currentRoute,
context
)
route: router.currentRoute,
ssrContext: context
})
})).then(() => {
isDev && console.log(`data pre-fetch: ${Date.now() - s}ms`)
// After all preFetch hooks are resolved, our store is now
......
......@@ -44,6 +44,6 @@ export default {
FETCH_USER: ({ commit, state }, { id }) => {
return state.users[id]
? Promise.resolve(state.users[id])
: fetchUser(id).then(user => commit('SET_USER', { user }))
: fetchUser(id).then(user => commit('SET_USER', { id, user }))
}
}
......@@ -17,7 +17,7 @@ export default {
})
},
SET_USER: (state, { user }) => {
Vue.set(state.users, user.id, user)
SET_USER: (state, { id, user }) => {
Vue.set(state.users, id, user || false) /* false means user not found */
}
}
......@@ -10,9 +10,9 @@ export default function createListView (type) {
return {
name: `${type}-stories-view`,
asyncData (store, route, context) {
asyncData ({ store, ssrContext }) {
return store.dispatch('FETCH_LIST_DATA', { type }).then(() => {
setTitle(camelize(type), context)
setTitle(camelize(type), ssrContext)
})
},
......
......@@ -46,20 +46,33 @@ export default {
}
},
asyncData (store, { params: { id }}, context) {
// We only fetch the item itself before entering the view, because
// it might take a long time to load threads with hundreds of comments
// due to how the HN Firebase API works.
asyncData ({ store, route: { params: { id }}, ssrContext }) {
return store.dispatch('FETCH_ITEMS', { ids: [id] }).then(() => {
const item = store.state.items[id]
setTitle(item.title, context)
setTitle(item.title, ssrContext)
})
},
// on the client, fetch all comments
// Fetch comments when mounted on the client
beforeMount () {
this.dataPromise.then(() => {
this.fetchComments()
},
// refetch comments if item changed
watch: {
item: 'fetchComments'
},
methods: {
fetchComments () {
this.loading = true
fetchComments(this.$store, this.item).then(() => {
this.loading = false
})
})
}
}
}
......
<template>
<div class="user-view">
<spinner :show="!user"></spinner>
<spinner :show="!userLoaded"></spinner>
<template v-if="user">
<h1>User : {{ user.id }}</h1>
<ul class="meta">
......@@ -13,6 +13,9 @@
<a :href="'https://news.ycombinator.com/threads?id=' + user.id">comments</a>
</p>
</template>
<template v-else-if="user === false">
<h1>User not found.</h1>
</template>
</div>
</template>
......@@ -27,13 +30,16 @@ export default {
computed: {
user () {
return this.$store.state.users[this.$route.params.id]
},
userLoaded () {
return this.$route.params.id in this.$store.state.users
}
},
asyncData (store, { params: { id }}, context) {
asyncData ({ store, route: { params: { id }}, ssrContext }) {
return store.dispatch('FETCH_USER', { id }).then(() => {
const user = store.state.users[id]
setTitle(user.id, context)
setTitle(user ? user.id : 'User not found', ssrContext)
})
}
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment