mirror of
https://gitee.com/vuejs/vue.git
synced 2024-12-04 21:17:55 +08:00
[Vue 2.0] Fix BooleanAttr & EnumeratedAttr serialization (#2810)
* fix attr serialization * Add attribute test cases
This commit is contained in:
parent
4ff27c9ab7
commit
ae78813606
@ -1,4 +1,4 @@
|
||||
import { isBooleanAttr, isEnumeratedAttr, isXlink, xlinkNS } from 'web/util/index'
|
||||
import { isBooleanAttr, isEnumeratedAttr, isXlink, xlinkNS, getXlinkProp } from 'web/util/index'
|
||||
|
||||
function updateAttrs (oldVnode, vnode) {
|
||||
if (!oldVnode.data.attrs && !vnode.data.attrs) {
|
||||
@ -19,8 +19,8 @@ function updateAttrs (oldVnode, vnode) {
|
||||
for (key in oldAttrs) {
|
||||
if (attrs[key] == null) {
|
||||
if (isXlink(key)) {
|
||||
elm.removeAttributeNS(xlinkNS, key)
|
||||
} else {
|
||||
elm.removeAttributeNS(xlinkNS, getXlinkProp(key))
|
||||
} else if (!isEnumeratedAttr(key)) {
|
||||
elm.removeAttribute(key)
|
||||
}
|
||||
}
|
||||
@ -29,17 +29,27 @@ function updateAttrs (oldVnode, vnode) {
|
||||
|
||||
function setAttr (el, key, value) {
|
||||
if (isBooleanAttr(key)) {
|
||||
if (value == null) {
|
||||
// set attribute for blank value
|
||||
// e.g. <option disabled>Select one</option>
|
||||
if (value == null || value === false) {
|
||||
el.removeAttribute(key)
|
||||
} else {
|
||||
el.setAttribute(key, key)
|
||||
}
|
||||
} else if (isEnumeratedAttr(key)) {
|
||||
el.setAttribute(key, value == null ? 'false' : 'true')
|
||||
el.setAttribute(key, value ? 'true' : 'false')
|
||||
} else if (isXlink(key)) {
|
||||
el.setAttributeNS(xlinkNS, key, value)
|
||||
if (value == null || value === false) {
|
||||
el.removeAttributeNS(xlinkNS, getXlinkProp(key))
|
||||
} else {
|
||||
el.setAttributeNS(xlinkNS, key, value === true ? '' : value)
|
||||
}
|
||||
} else {
|
||||
el.setAttribute(key, value)
|
||||
if (value == null || value === false) {
|
||||
el.removeAttribute(key)
|
||||
} else {
|
||||
el.setAttribute(key, value === true ? '' : value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -23,13 +23,16 @@ function serialize (attrs, asProps) {
|
||||
if (asProps) {
|
||||
key = propsToAttrMap[key] || key.toLowerCase()
|
||||
}
|
||||
if (attrs[key] != null) {
|
||||
if (isBooleanAttr(key)) {
|
||||
const value = attrs[key]
|
||||
if (isBooleanAttr(key)) {
|
||||
if (!(value == null || value === false)) {
|
||||
res += ` ${key}="${key}"`
|
||||
} else if (isEnumeratedAttr(key)) {
|
||||
res += ` ${key}="true"`
|
||||
} else {
|
||||
res += ` ${key}="${attrs[key]}"`
|
||||
}
|
||||
} else if (isEnumeratedAttr(key)) {
|
||||
res += ` ${key}="${value ? 'true' : 'false'}"`
|
||||
} else {
|
||||
if (!(value == null || value === false)) {
|
||||
res += ` ${key}="${value === true ? '' : value}"`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -23,3 +23,4 @@ export const propsToAttrMap = {
|
||||
|
||||
export const xlinkNS = 'http://www.w3.org/1999/xlink'
|
||||
export const isXlink = name => name.charAt(5) === ':' && name.slice(0, 5) === 'xlink'
|
||||
export const getXlinkProp = name => isXlink(name) ? name.slice(6, name.length) : ''
|
||||
|
@ -24,7 +24,7 @@ export function createStreamingRenderer (modules, directives, isUnaryTag) {
|
||||
if (isRoot) {
|
||||
if (!el.data) el.data = {}
|
||||
if (!el.data.attrs) el.data.attrs = {}
|
||||
el.data.attrs['server-rendered'] = true
|
||||
el.data.attrs['server-rendered'] = 'true'
|
||||
}
|
||||
const startTag = renderStartingTag(el, modules, directives)
|
||||
const endTag = `</${el.tag}>`
|
||||
|
@ -21,7 +21,7 @@ export function createSyncRenderer (modules, directives, isUnaryTag) {
|
||||
if (isRoot) {
|
||||
if (!el.data) el.data = {}
|
||||
if (!el.data.attrs) el.data.attrs = {}
|
||||
el.data.attrs['server-rendered'] = true
|
||||
el.data.attrs['server-rendered'] = 'true'
|
||||
}
|
||||
const startTag = renderStartingTag(el, modules, directives)
|
||||
const endTag = `</${el.tag}>`
|
||||
|
@ -125,6 +125,68 @@ describe('SSR: renderToString', () => {
|
||||
'</div>'
|
||||
)
|
||||
})
|
||||
|
||||
it('normal attr', () => {
|
||||
expect(renderVmWithOptions({
|
||||
template: `
|
||||
<div>
|
||||
<span :test="'ok'">hello</span>
|
||||
<span :test="null">hello</span>
|
||||
<span :test="false">hello</span>
|
||||
<span :test="true">hello</span>
|
||||
<span :test="0">hello</span>
|
||||
</div>
|
||||
`
|
||||
})).toContain(
|
||||
'<div server-rendered="true">' +
|
||||
'<span test="ok">hello</span>' +
|
||||
'<span>hello</span>' +
|
||||
'<span>hello</span>' +
|
||||
'<span test="">hello</span>' +
|
||||
'<span test="0">hello</span>' +
|
||||
'</div>'
|
||||
)
|
||||
})
|
||||
|
||||
it('enumrated attr', () => {
|
||||
expect(renderVmWithOptions({
|
||||
template: `
|
||||
<div>
|
||||
<span :draggable="true">hello</span>
|
||||
<span :draggable="'ok'">hello</span>
|
||||
<span :draggable="null">hello</span>
|
||||
<span :draggable="''">hello</span>
|
||||
</div>
|
||||
`
|
||||
})).toContain(
|
||||
'<div server-rendered="true">' +
|
||||
'<span draggable="true">hello</span>' +
|
||||
'<span draggable="true">hello</span>' +
|
||||
'<span draggable="false">hello</span>' +
|
||||
'<span draggable="true">hello</span>' +
|
||||
'</div>'
|
||||
)
|
||||
})
|
||||
|
||||
it('boolean attr', () => {
|
||||
expect(renderVmWithOptions({
|
||||
template: `
|
||||
<div>
|
||||
<span :disabled="true">hello</span>
|
||||
<span :disabled="'ok'">hello</span>
|
||||
<span :disabled="null">hello</span>
|
||||
<span :disabled="''">hello</span>
|
||||
</div>
|
||||
`
|
||||
})).toContain(
|
||||
'<div server-rendered="true">' +
|
||||
'<span disabled="disabled">hello</span>' +
|
||||
'<span disabled="disabled">hello</span>' +
|
||||
'<span>hello</span>' +
|
||||
'<span disabled="disabled">hello</span>' +
|
||||
'</div>'
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
function renderVmWithOptions (options) {
|
||||
|
@ -0,0 +1,115 @@
|
||||
import Vue from 'vue'
|
||||
|
||||
describe('Directive v-bind', () => {
|
||||
it('normal attr', done => {
|
||||
const vm = new Vue({
|
||||
el: '#app',
|
||||
template: '<div><span :test="foo">hello</span></div>',
|
||||
data: { foo: 'ok' }
|
||||
})
|
||||
expect(vm.$el.firstChild.getAttribute('test')).toBe('ok')
|
||||
vm.foo = 'again'
|
||||
waitForUpdate(() => {
|
||||
expect(vm.$el.firstChild.getAttribute('test')).toBe('again')
|
||||
vm.foo = null
|
||||
}).then(() => {
|
||||
expect(vm.$el.firstChild.hasAttribute('test')).toBe(false)
|
||||
vm.foo = false
|
||||
}).then(() => {
|
||||
expect(vm.$el.firstChild.hasAttribute('test')).toBe(false)
|
||||
vm.foo = true
|
||||
}).then(() => {
|
||||
expect(vm.$el.firstChild.getAttribute('test')).toBe('')
|
||||
vm.foo = 0
|
||||
}).then(() => {
|
||||
expect(vm.$el.firstChild.getAttribute('test')).toBe('0')
|
||||
done()
|
||||
}).catch(done)
|
||||
})
|
||||
|
||||
it('should set property for input value', done => {
|
||||
const vm = new Vue({
|
||||
el: '#app',
|
||||
template: `
|
||||
<div>
|
||||
<input type="text" :value="foo">
|
||||
<input type="checkbox" :checked="bar">
|
||||
</div>
|
||||
`,
|
||||
data: {
|
||||
foo: 'ok',
|
||||
bar: false
|
||||
}
|
||||
})
|
||||
expect(vm.$el.firstChild.value).toBe('ok')
|
||||
expect(vm.$el.lastChild.checked).toBe(false)
|
||||
vm.bar = true
|
||||
waitForUpdate(() => {
|
||||
expect(vm.$el.lastChild.checked).toBe(true)
|
||||
done()
|
||||
}).catch(done)
|
||||
})
|
||||
|
||||
it('xlink', done => {
|
||||
const vm = new Vue({
|
||||
el: '#app',
|
||||
template: '<svg><a :xlink:special="foo"></a></svg>',
|
||||
data: {
|
||||
foo: 'ok'
|
||||
}
|
||||
})
|
||||
const xlinkNS = 'http://www.w3.org/1999/xlink'
|
||||
expect(vm.$el.firstChild.getAttributeNS(xlinkNS, 'special')).toBe('ok')
|
||||
vm.foo = 'again'
|
||||
waitForUpdate(() => {
|
||||
expect(vm.$el.firstChild.getAttributeNS(xlinkNS, 'special')).toBe('again')
|
||||
vm.foo = null
|
||||
}).then(() => {
|
||||
expect(vm.$el.firstChild.hasAttributeNS(xlinkNS, 'special')).toBe(false)
|
||||
vm.foo = true
|
||||
}).then(() => {
|
||||
expect(vm.$el.firstChild.getAttributeNS(xlinkNS, 'special')).toBe('')
|
||||
done()
|
||||
}).catch(done)
|
||||
})
|
||||
|
||||
it('enumrated attr', done => {
|
||||
const vm = new Vue({
|
||||
el: '#app',
|
||||
template: '<div><span :draggable="foo">hello</span></div>',
|
||||
data: { foo: true }
|
||||
})
|
||||
expect(vm.$el.firstChild.getAttribute('draggable')).toBe('true')
|
||||
vm.foo = 'again'
|
||||
waitForUpdate(() => {
|
||||
expect(vm.$el.firstChild.getAttribute('draggable')).toBe('true')
|
||||
vm.foo = null
|
||||
}).then(() => {
|
||||
expect(vm.$el.firstChild.getAttribute('draggable')).toBe('false')
|
||||
vm.foo = ''
|
||||
}).then(() => {
|
||||
expect(vm.$el.firstChild.getAttribute('draggable')).toBe('false')
|
||||
done()
|
||||
}).catch(done)
|
||||
})
|
||||
|
||||
it('boolean attr', done => {
|
||||
const vm = new Vue({
|
||||
el: '#app',
|
||||
template: '<div><span :disabled="foo">hello</span></div>',
|
||||
data: { foo: true }
|
||||
})
|
||||
expect(vm.$el.firstChild.getAttribute('disabled')).toBe('disabled')
|
||||
vm.foo = 'again'
|
||||
waitForUpdate(() => {
|
||||
expect(vm.$el.firstChild.getAttribute('disabled')).toBe('disabled')
|
||||
vm.foo = null
|
||||
}).then(() => {
|
||||
expect(vm.$el.firstChild.hasAttribute('disabled')).toBe(false)
|
||||
vm.foo = ''
|
||||
}).then(() => {
|
||||
expect(vm.$el.firstChild.hasAttribute('disabled')).toBe(true)
|
||||
done()
|
||||
}).catch(done)
|
||||
})
|
||||
})
|
Loading…
Reference in New Issue
Block a user