Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(router-view): add passing props to inactive component #2773

Merged
merged 4 commits into from
Jan 15, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 67 additions & 1 deletion examples/keepalive-view/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,52 @@ const IndexChild2 = { template: '<div>index child2</div>' }

const Home = { template: '<div>home</div>' }

const ViewWithKeepalive = { template: '<keep-alive><router-view></router-view></keep-alive>' }
const ViewWithKeepalive = {
template: '<keep-alive><router-view></router-view></keep-alive>'
}

const Parent = { template: '<div>msg: {{ msg }}<router-view /></div>', props: ['msg'] }

const RequiredProps = {
template: '<div>props from route config is: {{ msg }}</div>',
props: {
msg: {
type: String,
required: true
}
}
}

// keep original values to restore them later
const originalSilent = Vue.config.silent
const originalWarnHandler = Vue.config.warnHandler

const CatchWarn = {
template: `<div>{{ didWarn ? 'caught missing prop warn' : 'no missing prop warn' }}</div>`,
data () {
return {
didWarn: false
}
},
beforeRouteEnter (to, from, next) {
let missPropWarn = false
Vue.config.silent = false
Vue.config.warnHandler = function (msg, vm, trace) {
if (/Missing required prop/i.test(msg)) {
missPropWarn = true
}
}
next(vm => {
vm.didWarn = missPropWarn
})
},
beforeRouteLeave (to, from, next) {
// restore vue config
Vue.config.silent = originalSilent
Vue.config.warnHandler = originalWarnHandler
next()
}
}

const router = new VueRouter({
mode: 'history',
Expand Down Expand Up @@ -80,6 +125,24 @@ const router = new VueRouter({
]
}
]
},
{
path: '/config-required-props',
component: Parent,
props: { msg: 'from parent' },
children: [
{
path: 'child',
component: RequiredProps,
props: {
msg: 'from child'
}
}
]
},
{
path: '/catch-warn',
component: CatchWarn
}
]
})
Expand All @@ -96,6 +159,9 @@ new Vue({
<li><router-link to="/with-guard2">/with-guard2</router-link></li>
<li><router-link to="/one/two/child1">/one/two/child1</router-link></li>
<li><router-link to="/one/two/child2">/one/two/child2</router-link></li>
<li><router-link to="/config-required-props">/config-required-props</router-link></li>
<li><router-link to="/config-required-props/child">/config-required-props/child</router-link></li>
<li><router-link to="/catch-warn">/catch-warn</router-link></li>
</ul>
<keep-alive>
<router-view class="view"></router-view>
Expand Down
61 changes: 44 additions & 17 deletions src/components/view.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,17 +39,32 @@ export default {

// render previous view if the tree is inactive and kept-alive
if (inactive) {
return h(cache[name], data, children)
const cachedData = cache[name]
const cachedComponent = cachedData && cachedData.component
if (cachedComponent) {
// #2301
// pass props
if (cachedData.configProps) {
fillPropsinData(cachedComponent, data, cachedData.route, cachedData.configProps)
}
return h(cachedComponent, data, children)
} else {
// render previous empty view
return h()
}
}

const matched = route.matched[depth]
// render empty node if no matched route
if (!matched) {
const component = matched && matched.components[name]

// render empty node if no matched route or no config component
if (!matched || !component) {
cache[name] = null
return h()
}

const component = cache[name] = matched.components[name]
// cache component
cache[name] = { component }

// attach instance registration hook
// this will be called in the instance's injected lifecycle hooks
Expand Down Expand Up @@ -81,25 +96,37 @@ export default {
}
}

// resolve props
let propsToPass = data.props = resolveProps(route, matched.props && matched.props[name])
if (propsToPass) {
// clone to prevent mutation
propsToPass = data.props = extend({}, propsToPass)
// pass non-declared props as attrs
const attrs = data.attrs = data.attrs || {}
for (const key in propsToPass) {
if (!component.props || !(key in component.props)) {
attrs[key] = propsToPass[key]
delete propsToPass[key]
}
}
const configProps = matched.props && matched.props[name]
// save route and configProps in cachce
if (configProps) {
extend(cache[name], {
route,
configProps
})
fillPropsinData(component, data, route, configProps)
}

return h(component, data, children)
}
}

function fillPropsinData (component, data, route, configProps) {
// resolve props
let propsToPass = data.props = resolveProps(route, configProps)
if (propsToPass) {
// clone to prevent mutation
propsToPass = data.props = extend({}, propsToPass)
// pass non-declared props as attrs
const attrs = data.attrs = data.attrs || {}
for (const key in propsToPass) {
if (!component.props || !(key in component.props)) {
attrs[key] = propsToPass[key]
delete propsToPass[key]
}
}
}
}

function resolveProps (route, config) {
switch (typeof config) {
case 'undefined':
Expand Down
13 changes: 12 additions & 1 deletion test/e2e/specs/keepalive-view.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ module.exports = {
browser
.url('http://localhost:8080/keepalive-view/')
.waitForElementVisible('#app', 1000)
.assert.count('li a', 7)
.assert.count('li a', 10)

.click('li:nth-child(1) a')
.assert.containsText('.view', 'index child1')
Expand Down Expand Up @@ -44,6 +44,17 @@ module.exports = {
.click('li:nth-child(7) a')
.assert.containsText('.view', 'index child2')

// missing props in nested routes with keep alive
// https://github.com/vuejs/vue-router/issues/2301
.click('li:nth-child(8) a')
.assert.containsText('.view', 'msg: from parent')
.click('li:nth-child(9) a')
.assert.containsText('.view', 'msg: from parent\nprops from route config is: from child')
.click('li:nth-child(10) a')
.assert.containsText('.view', 'no missing prop warn')
.click('li:nth-child(9) a')
.assert.containsText('.view', 'msg: from parent\nprops from route config is: from child')

.end()
}
}