mirror of
https://gitee.com/vuejs/vue.git
synced 2024-11-30 11:07:51 +08:00
parent
e80104eb63
commit
e1657fd7ce
@ -1,5 +1,7 @@
|
||||
import show from './show'
|
||||
import model from './model'
|
||||
|
||||
export default {
|
||||
show
|
||||
show,
|
||||
model
|
||||
}
|
||||
|
44
src/platforms/web/server/directives/model.js
Normal file
44
src/platforms/web/server/directives/model.js
Normal file
@ -0,0 +1,44 @@
|
||||
/* @flow */
|
||||
|
||||
import { looseEqual, looseIndexOf } from 'shared/util'
|
||||
|
||||
// this is only applied for <select v-model> because it is the only edge case
|
||||
// that must be done at runtime instead of compile time.
|
||||
export default function model (node: VNodeWithData, dir: VNodeDirective) {
|
||||
if (!node.children) return
|
||||
const value = dir.value
|
||||
const isMultiple = node.data.attrs && node.data.attrs.multiple
|
||||
for (let i = 0, l = node.children.length; i < l; i++) {
|
||||
const option = node.children[i]
|
||||
if (option.tag === 'option') {
|
||||
if (isMultiple) {
|
||||
const selected =
|
||||
Array.isArray(value) &&
|
||||
(looseIndexOf(value, getValue(option)) > -1)
|
||||
if (selected) {
|
||||
setSelected(option)
|
||||
}
|
||||
} else {
|
||||
if (looseEqual(value, getValue(option))) {
|
||||
setSelected(option)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getValue (option) {
|
||||
const data = option.data || {}
|
||||
return (
|
||||
(data.attrs && data.attrs.value) ||
|
||||
(data.domProps && data.domProps.value) ||
|
||||
(option.children && option.children[0] && option.children[0].text)
|
||||
)
|
||||
}
|
||||
|
||||
function setSelected (option) {
|
||||
const data = option.data || (option.data = {})
|
||||
const attrs = data.attrs || (data.attrs = {})
|
||||
attrs.selected = ''
|
||||
}
|
@ -113,7 +113,8 @@ function isUnOptimizableTree (node: ASTNode): boolean {
|
||||
return (
|
||||
isBuiltInTag(node.tag) || // built-in (slot, component)
|
||||
!isPlatformReservedTag(node.tag) || // custom component
|
||||
!!node.component // "is" component
|
||||
!!node.component || // "is" component
|
||||
isSelectWithModel(node) // <select v-model> requires runtime inspection
|
||||
)
|
||||
}
|
||||
|
||||
@ -126,3 +127,14 @@ function hasCustomDirective (node: ASTNode): ?boolean {
|
||||
node.directives.some(d => !isBuiltInDir(d.name))
|
||||
)
|
||||
}
|
||||
|
||||
// <select v-model> cannot be optimized because it requires a runtime check
|
||||
// to determine proper selected option
|
||||
function isSelectWithModel (node: ASTNode): ?boolean {
|
||||
return (
|
||||
node.type === 1 &&
|
||||
node.tag === 'select' &&
|
||||
node.directives &&
|
||||
node.directives.some(d => d.name === 'model')
|
||||
)
|
||||
}
|
||||
|
@ -1034,6 +1034,84 @@ describe('SSR: renderToString', () => {
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('render v-model with <select> (value binding)', done => {
|
||||
renderVmWithOptions({
|
||||
data: {
|
||||
selected: 2,
|
||||
options: [
|
||||
{ id: 1, label: 'one' },
|
||||
{ id: 2, label: 'two' }
|
||||
]
|
||||
},
|
||||
template: `
|
||||
<div>
|
||||
<select v-model="selected">
|
||||
<option v-for="o in options" :value="o.id">{{ o.label }}</option>
|
||||
</select>
|
||||
</div>
|
||||
`
|
||||
}, result => {
|
||||
expect(result).toContain(
|
||||
'<select>' +
|
||||
'<option value="1">one</option>' +
|
||||
'<option selected="selected" value="2">two</option>' +
|
||||
'</select>'
|
||||
)
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('render v-model with <select> (static value)', done => {
|
||||
renderVmWithOptions({
|
||||
data: {
|
||||
selected: 2
|
||||
},
|
||||
template: `
|
||||
<div>
|
||||
<select v-model="selected">
|
||||
<option value="1">one</option>
|
||||
<option value="2">two</option>
|
||||
</select>
|
||||
</div>
|
||||
`
|
||||
}, result => {
|
||||
expect(result).toContain(
|
||||
'<select>' +
|
||||
'<option value="1">one</option> ' +
|
||||
'<option value="2" selected="selected">two</option>' +
|
||||
'</select>'
|
||||
)
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('render v-model with <select> (text as value)', done => {
|
||||
renderVmWithOptions({
|
||||
data: {
|
||||
selected: 2,
|
||||
options: [
|
||||
{ id: 1, label: 'one' },
|
||||
{ id: 2, label: 'two' }
|
||||
]
|
||||
},
|
||||
template: `
|
||||
<div>
|
||||
<select v-model="selected">
|
||||
<option v-for="o in options">{{ o.id }}</option>
|
||||
</select>
|
||||
</div>
|
||||
`
|
||||
}, result => {
|
||||
expect(result).toContain(
|
||||
'<select>' +
|
||||
'<option>1</option>' +
|
||||
'<option selected="selected">2</option>' +
|
||||
'</select>'
|
||||
)
|
||||
done()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
function renderVmWithOptions (options, cb) {
|
||||
|
Loading…
Reference in New Issue
Block a user