Skip to content

Commit

Permalink
sd-model
Browse files Browse the repository at this point in the history
  • Loading branch information
yyx990803 committed Oct 13, 2013
1 parent 1e90903 commit b8781c5
Show file tree
Hide file tree
Showing 4 changed files with 197 additions and 117 deletions.
1 change: 0 additions & 1 deletion TODO.md
@@ -1,4 +1,3 @@
- tests for sd-model, remove sd-value and sd-checked
- add escape: {{{ things in here should not be parsed }}}
- sd-transition
- component examples
Expand Down
1 change: 1 addition & 0 deletions src/compiler.js
Expand Up @@ -537,6 +537,7 @@ CompilerProto.destroy = function () {
utils.log('compiler destroyed: ', compiler.vm.$el)
// unwatch
compiler.observer.off()
compiler.emitter.off()
var i, key, dir, inss, binding,
el = compiler.el,
directives = compiler.dirs,
Expand Down
64 changes: 18 additions & 46 deletions src/directives/index.js
Expand Up @@ -8,15 +8,11 @@ module.exports = {
},

text: function (value) {
this.el.textContent = isValidTextValue(value)
? value
: ''
this.el.textContent = toText(value)
},

html: function (value) {
this.el.innerHTML = isValidTextValue(value)
? value
: ''
this.el.innerHTML = toText(value)
},

style: {
Expand Down Expand Up @@ -57,38 +53,6 @@ module.exports = {
}
},

value: {
bind: function () {
var el = this.el, self = this
this.change = function () {
self.vm.$set(self.key, el.value)
}
el.addEventListener('keyup', this.change)
},
update: function (value) {
this.el.value = value ? value : ''
},
unbind: function () {
this.el.removeEventListener('keyup', this.change)
}
},

checked: {
bind: function () {
var el = this.el, self = this
this.change = function () {
self.vm.$set(self.key, el.checked)
}
el.addEventListener('change', this.change)
},
update: function (value) {
this.el.checked = !!value
},
unbind: function () {
this.el.removeEventListener('change', this.change)
}
},

model: {
bind: function () {
var self = this,
Expand All @@ -97,8 +61,8 @@ module.exports = {
lazy = self.compiler.options.lazy
self.event =
(lazy ||
el.tagName === 'SELECT' ||
type === 'checkbox' ||
type === 'select' ||
type === 'radio')
? 'change'
: 'keyup'
Expand All @@ -111,11 +75,14 @@ module.exports = {
el.addEventListener(self.event, self.set)
},
update: function (value) {
this.el[this.attr] = this.attr === 'checked'
? !!value
: isValidTextValue(value)
? value
: ''
if (this.el.type === 'radio') {
/* jshint eqeqeq: false */
this.el.checked = value == this.el.value
} else {
this.el[this.attr] = this.attr === 'checked'
? !!value
: toText(value)
}
},
unbind: function () {
this.el.removeEventListener(this.event, this.set)
Expand Down Expand Up @@ -167,6 +134,11 @@ function convertCSSProperty (prop) {
})
}

function isValidTextValue (value) {
return typeof value === 'string' || typeof value === 'number'
/*
* Make sure only strings and numbers are output to html
*/
function toText (value) {
return (typeof value === 'string' || typeof value === 'number')
? value
: ''
}
248 changes: 178 additions & 70 deletions test/unit/specs/directives.js
Expand Up @@ -213,89 +213,191 @@ describe('UNIT: Directives', function () {

})

describe('value', function () {

var dir = mockDirective('value', 'input')
dir.bind()

before(function () {
document.body.appendChild(dir.el)
})
describe('model', function () {

it('should set the value on update()', function () {
dir.update('foobar')
assert.strictEqual(dir.el.value, 'foobar')
})
describe('input[checkbox]', function () {

it('should trigger vm.$set when value is changed via keyup', function () {
var triggered = false
dir.key = 'foo'
dir.vm = { $set: function (key, val) {
assert.strictEqual(key, 'foo')
assert.strictEqual(val, 'bar')
triggered = true
}}
dir.el.value = 'bar'
dir.el.dispatchEvent(mockKeyEvent('keyup'))
assert.ok(triggered)
})
var dir = mockDirective('model', 'input', 'checkbox')
dir.bind()

it('should remove event listener with unbind()', function () {
var removed = true
dir.vm.$set = function () {
removed = false
}
dir.unbind()
dir.el.dispatchEvent(mockKeyEvent('keyup'))
assert.ok(removed)
})
before(function () {
document.body.appendChild(dir.el)
})

after(function () {
document.body.removeChild(dir.el)
})
it('should set checked on update()', function () {
dir.update(true)
assert.ok(dir.el.checked)
dir.update(false)
assert.ok(!dir.el.checked)
})

})
it('should trigger vm.$set when clicked', function () {
var triggered = false
dir.key = 'foo'
dir.vm = { $set: function (key, val) {
assert.strictEqual(key, 'foo')
assert.strictEqual(val, true)
triggered = true
}}
dir.el.dispatchEvent(mockMouseEvent('click'))
assert.ok(triggered)
})

describe('checked', function () {

var dir = mockDirective('checked', 'input', 'checkbox')
dir.bind()
it('should remove event listener with unbind()', function () {
var removed = true
dir.vm.$set = function () {
removed = false
}
dir.unbind()
dir.el.dispatchEvent(mockMouseEvent('click'))
assert.ok(removed)
})

before(function () {
document.body.appendChild(dir.el)
})
after(function () {
document.body.removeChild(dir.el)
})

it('should set checked on update()', function () {
dir.update(true)
assert.ok(dir.el.checked)
dir.update(false)
assert.ok(!dir.el.checked)
})

it('should trigger vm.$set on change event', function () {
var triggered = false
dir.key = 'foo'
dir.vm = { $set: function (key, val) {
assert.strictEqual(key, 'foo')
assert.strictEqual(val, true)
triggered = true
}}
dir.el.dispatchEvent(mockMouseEvent('click'))
assert.ok(triggered)
describe('input[radio]', function () {

var dir1 = mockDirective('model', 'input', 'radio'),
dir2 = mockDirective('model', 'input', 'radio')
dir1.el.name = 'input-radio'
dir2.el.name = 'input-radio'
dir1.el.value = '12345'
dir2.el.value = '54321'
dir1.bind()
dir2.bind()

before(function () {
document.body.appendChild(dir1.el)
document.body.appendChild(dir2.el)
})

it('should set el.checked on update()', function () {
assert.notOk(dir1.el.checked)
dir1.update(12345)
assert.ok(dir1.el.checked)
})

it('should trigger vm.$set when clicked', function () {
var triggered = false
dir2.key = 'radio'
dir2.vm = { $set: function (key, val) {
triggered = true
assert.strictEqual(key, 'radio')
assert.strictEqual(val, dir2.el.value)
}}
dir2.el.dispatchEvent(mockMouseEvent('click'))
assert.ok(triggered)
assert.ok(dir2.el.checked)
assert.notOk(dir1.el.checked)
})

it('should remove listeners on unbind()', function () {
var removed = true
dir1.vm = { $set: function () {
removed = false
}}
dir1.unbind()
dir1.el.dispatchEvent(mockMouseEvent('click'))
assert.ok(removed)
})

after(function () {
document.body.removeChild(dir1.el)
document.body.removeChild(dir2.el)
})

})

it('should remove event listener with unbind()', function () {
var removed = true
dir.vm.$set = function () {
removed = false
}
dir.unbind()
dir.el.dispatchEvent(mockMouseEvent('click'))
assert.ok(removed)
describe('select', function () {

var dir = mockDirective('model', 'select')
dir.el.innerHTML = '<option>0</option><option>1</option>'
dir.bind()

before(function () {
document.body.appendChild(dir.el)
})

it('should set value on update()', function () {
dir.update(0)
assert.strictEqual(dir.el.value, '0')
})

it('should trigger vm.$set when value is changed', function () {
var triggered = false
dir.key = 'select'
dir.vm = { $set: function (key, val) {
triggered = true
assert.strictEqual(key, 'select')
assert.equal(val, 1)
}}
dir.el.options.selectedIndex = 1
dir.el.dispatchEvent(mockChangeEvent())
assert.ok(triggered)
})

it('should remove listener on unbind()', function () {
var removed = true
dir.vm = { $set: function () {
removed = false
}}
dir.unbind()
dir.el.dispatchEvent(mockChangeEvent())
assert.ok(removed)
})

after(function () {
document.body.removeChild(dir.el)
})

})

describe('input[text] and others', function () {

var dir = mockDirective('model', 'input', 'email')
dir.bind()

before(function () {
document.body.appendChild(dir.el)
})

it('should set the value on update()', function () {
dir.update('foobar')
assert.strictEqual(dir.el.value, 'foobar')
})

// `lazy` option is tested in the API suite
it('should trigger vm.$set when value is changed via keyup', function () {
var triggered = false
dir.key = 'foo'
dir.vm = { $set: function (key, val) {
assert.strictEqual(key, 'foo')
assert.strictEqual(val, 'bar')
triggered = true
}}
dir.el.value = 'bar'
dir.el.dispatchEvent(mockKeyEvent('keyup'))
assert.ok(triggered)
})

it('should remove event listener with unbind()', function () {
var removed = true
dir.vm.$set = function () {
removed = false
}
dir.unbind()
dir.el.dispatchEvent(mockKeyEvent('keyup'))
assert.ok(removed)
})

after(function () {
document.body.removeChild(dir.el)
})

after(function () {
document.body.removeChild(dir.el)
})

})
Expand Down Expand Up @@ -441,7 +543,7 @@ function mockDirective (dirName, tag, type) {
var dir = Seed.directive(dirName),
ret = {
binding: { compiler: { vm: {} } },
compiler: { vm: {} },
compiler: { vm: {}, options: {} },
el: document.createElement(tag || 'div')
}
if (typeof dir === 'function') {
Expand All @@ -455,6 +557,12 @@ function mockDirective (dirName, tag, type) {
return ret
}

function mockChangeEvent () {
var e = document.createEvent('HTMLEvents')
e.initEvent('change', true, true)
return e
}

function mockKeyEvent (type) {
var e = document.createEvent('KeyboardEvent'),
initMethod = e.initKeyboardEvent
Expand Down

0 comments on commit b8781c5

Please sign in to comment.