directives.js 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767
  1. describe('UNIT: Directives', function () {
  2. describe('attr', function () {
  3. var dir = mockDirective('attr', 'input'),
  4. el = dir.el
  5. it('should set a truthy attribute value', function () {
  6. var value = 'Arrrrrr!'
  7. dir.arg = 'value'
  8. dir.update(value)
  9. assert.strictEqual(el.getAttribute('value'), value)
  10. })
  11. it('should set attribute value to `0`', function () {
  12. dir.arg = 'value'
  13. dir.update(0)
  14. assert.strictEqual(el.getAttribute('value'), '0')
  15. })
  16. it('should remove an attribute if value is `false`', function () {
  17. dir.arg = 'disabled'
  18. el.setAttribute('disabled', 'disabled')
  19. dir.update(false)
  20. assert.strictEqual(el.getAttribute('disabled'), null)
  21. })
  22. it('should remove an attribute if value is `null`', function () {
  23. dir.arg = 'disabled'
  24. el.setAttribute('disabled', 'disabled')
  25. dir.update(null)
  26. assert.strictEqual(el.getAttribute('disabled'), null)
  27. })
  28. it('should remove an attribute if value is `undefined`', function () {
  29. dir.arg = 'disabled'
  30. el.setAttribute('disabled', 'disabled')
  31. dir.update(undefined)
  32. assert.strictEqual(el.getAttribute('disabled'), null)
  33. })
  34. })
  35. describe('text', function () {
  36. var dir = mockDirective('text')
  37. it('should work with a string', function () {
  38. dir.update('hallo')
  39. assert.strictEqual(dir.el.textContent, 'hallo')
  40. })
  41. it('should work with a number', function () {
  42. dir.update(12345)
  43. assert.strictEqual(dir.el.textContent, '12345')
  44. })
  45. it('should work with booleans', function () {
  46. dir.update(true)
  47. assert.strictEqual(dir.el.textContent, 'true')
  48. })
  49. it('should work with objects', function () {
  50. dir.update({foo:"bar"})
  51. assert.strictEqual(dir.el.textContent, '{"foo":"bar"}')
  52. })
  53. it('should be empty with other stuff', function () {
  54. dir.update(null)
  55. assert.strictEqual(dir.el.textContent, '')
  56. dir.update(undefined)
  57. assert.strictEqual(dir.el.textContent, '')
  58. dir.update(function () {})
  59. assert.strictEqual(dir.el.textContent, '')
  60. })
  61. })
  62. describe('html', function () {
  63. var dir = mockDirective('html')
  64. it('should work with a string', function () {
  65. dir.update('hi!!!')
  66. assert.strictEqual(dir.el.innerHTML, 'hi!!!')
  67. dir.update('<span>haha</span><a>lol</a>')
  68. assert.strictEqual(dir.el.querySelector('span').textContent, 'haha')
  69. })
  70. it('should work with a number', function () {
  71. dir.update(12345)
  72. assert.strictEqual(dir.el.innerHTML, '12345')
  73. })
  74. it('should work with booleans', function () {
  75. dir.update(true)
  76. assert.strictEqual(dir.el.textContent, 'true')
  77. })
  78. it('should work with objects', function () {
  79. dir.update({foo:"bar"})
  80. assert.strictEqual(dir.el.textContent, '{"foo":"bar"}')
  81. })
  82. it('should be empty with other stuff', function () {
  83. dir.update(null)
  84. assert.strictEqual(dir.el.innerHTML, '')
  85. dir.update(undefined)
  86. assert.strictEqual(dir.el.innerHTML, '')
  87. dir.update(function () {})
  88. assert.strictEqual(dir.el.innerHTML, '')
  89. })
  90. it('should swap html if el is a comment placeholder', function () {
  91. var dir = mockDirective('html'),
  92. comment = document.createComment('hi'),
  93. parent = dir.el
  94. parent.innerHTML = 'what!'
  95. parent.appendChild(comment)
  96. dir.el = comment
  97. dir.bind()
  98. assert.ok(dir.holder)
  99. assert.ok(dir.nodes)
  100. var pre = 'what!',
  101. after = '<!--hi-->',
  102. h1 = '<span>hello</span><span>world</span>',
  103. h2 = '<a>whats</a><a>up</a>'
  104. dir.update(h1)
  105. assert.strictEqual(parent.innerHTML, pre + h1 + after)
  106. dir.update(h2)
  107. assert.strictEqual(parent.innerHTML, pre + h2 + after)
  108. })
  109. })
  110. describe('show', function () {
  111. var dir = mockDirective('show')
  112. it('should be default value when value is truthy', function () {
  113. dir.update(1)
  114. assert.strictEqual(dir.el.style.display, '')
  115. dir.update('hi!')
  116. assert.strictEqual(dir.el.style.display, '')
  117. dir.update(true)
  118. assert.strictEqual(dir.el.style.display, '')
  119. dir.update({})
  120. assert.strictEqual(dir.el.style.display, '')
  121. dir.update(function () {})
  122. assert.strictEqual(dir.el.style.display, '')
  123. })
  124. it('should be none when value is falsy', function () {
  125. dir.update(0)
  126. assert.strictEqual(dir.el.style.display, 'none')
  127. dir.update('')
  128. assert.strictEqual(dir.el.style.display, 'none')
  129. dir.update(false)
  130. assert.strictEqual(dir.el.style.display, 'none')
  131. dir.update(null)
  132. assert.strictEqual(dir.el.style.display, 'none')
  133. dir.update(undefined)
  134. assert.strictEqual(dir.el.style.display, 'none')
  135. })
  136. })
  137. describe('class', function () {
  138. function contains (el, cls) {
  139. var cur = ' ' + el.className + ' '
  140. return cur.indexOf(' ' + cls + ' ') > -1
  141. }
  142. it('should set class to the value if it has no arg', function () {
  143. var dir = mockDirective('class')
  144. dir.update('test')
  145. assert.ok(contains(dir.el, 'test'))
  146. dir.update('hoho')
  147. assert.ok(!contains(dir.el, 'test'))
  148. assert.ok(contains(dir.el, 'hoho'))
  149. })
  150. it('should add/remove class based on truthy/falsy if it has an arg', function () {
  151. var dir = mockDirective('class')
  152. dir.arg = 'test'
  153. dir.update(true)
  154. assert.ok(contains(dir.el, 'test'))
  155. dir.update(false)
  156. assert.ok(!contains(dir.el, 'test'))
  157. })
  158. })
  159. describe('model', function () {
  160. describe('input[checkbox]', function () {
  161. var dir = mockDirective('model', 'input', 'checkbox')
  162. dir.bind()
  163. before(function () {
  164. document.body.appendChild(dir.el)
  165. })
  166. it('should set checked on update()', function () {
  167. dir.update(true)
  168. assert.ok(dir.el.checked)
  169. dir.update(false)
  170. assert.ok(!dir.el.checked)
  171. })
  172. it('should trigger vm.$set when clicked', function () {
  173. var triggered = false
  174. dir.key = 'foo'
  175. dir.vm = { $set: function (key, val) {
  176. assert.strictEqual(key, 'foo')
  177. assert.strictEqual(val, true)
  178. triggered = true
  179. }}
  180. dir.el.dispatchEvent(mockMouseEvent('click'))
  181. assert.ok(triggered)
  182. })
  183. it('should remove event listener with unbind()', function () {
  184. var removed = true
  185. dir.vm.$set = function () {
  186. removed = false
  187. }
  188. dir.unbind()
  189. dir.el.dispatchEvent(mockMouseEvent('click'))
  190. assert.ok(removed)
  191. })
  192. after(function () {
  193. document.body.removeChild(dir.el)
  194. })
  195. })
  196. describe('input[radio]', function () {
  197. var dir1 = mockDirective('model', 'input', 'radio'),
  198. dir2 = mockDirective('model', 'input', 'radio')
  199. dir1.el.name = 'input-radio'
  200. dir2.el.name = 'input-radio'
  201. dir1.el.value = '12345'
  202. dir2.el.value = '54321'
  203. dir1.bind()
  204. dir2.bind()
  205. before(function () {
  206. document.body.appendChild(dir1.el)
  207. document.body.appendChild(dir2.el)
  208. })
  209. it('should set el.checked on update()', function () {
  210. assert.notOk(dir1.el.checked)
  211. dir1.update(12345)
  212. assert.ok(dir1.el.checked)
  213. })
  214. it('should trigger vm.$set when clicked', function () {
  215. var triggered = false
  216. dir2.key = 'radio'
  217. dir2.vm = { $set: function (key, val) {
  218. triggered = true
  219. assert.strictEqual(key, 'radio')
  220. assert.strictEqual(val, dir2.el.value)
  221. }}
  222. dir2.el.dispatchEvent(mockMouseEvent('click'))
  223. assert.ok(triggered)
  224. assert.ok(dir2.el.checked)
  225. assert.notOk(dir1.el.checked)
  226. })
  227. it('should remove listeners on unbind()', function () {
  228. var removed = true
  229. dir1.vm = { $set: function () {
  230. removed = false
  231. }}
  232. dir1.unbind()
  233. dir1.el.dispatchEvent(mockMouseEvent('click'))
  234. assert.ok(removed)
  235. })
  236. after(function () {
  237. document.body.removeChild(dir1.el)
  238. document.body.removeChild(dir2.el)
  239. })
  240. })
  241. describe('select', function () {
  242. var dir = mockDirective('model', 'select'),
  243. o1 = document.createElement('option'),
  244. o2 = document.createElement('option')
  245. o1.value = 0
  246. o2.value = 1
  247. dir.el.appendChild(o1)
  248. dir.el.appendChild(o2)
  249. dir.bind()
  250. before(function () {
  251. document.body.appendChild(dir.el)
  252. })
  253. it('should set value on update()', function () {
  254. dir.update(0)
  255. assert.strictEqual(dir.el.value, '0')
  256. })
  257. it('should trigger vm.$set when value is changed', function () {
  258. var triggered = false
  259. dir.key = 'select'
  260. dir.vm = { $set: function (key, val) {
  261. triggered = true
  262. assert.strictEqual(key, 'select')
  263. assert.equal(val, 1)
  264. }}
  265. dir.el.options.selectedIndex = 1
  266. dir.el.dispatchEvent(mockHTMLEvent('change'))
  267. assert.ok(triggered)
  268. })
  269. it('should remove listener on unbind()', function () {
  270. var removed = true
  271. dir.vm = { $set: function () {
  272. removed = false
  273. }}
  274. dir.unbind()
  275. dir.el.dispatchEvent(mockHTMLEvent('change'))
  276. assert.ok(removed)
  277. })
  278. after(function () {
  279. document.body.removeChild(dir.el)
  280. })
  281. })
  282. describe('input[text] and others', function () {
  283. var dir = mockDirective('model', 'input', 'text')
  284. dir.bind()
  285. before(function () {
  286. document.body.appendChild(dir.el)
  287. })
  288. it('should set the value on update()', function () {
  289. dir.update('foobar')
  290. assert.strictEqual(dir.el.value, 'foobar')
  291. })
  292. // `lazy` option is tested in the API suite
  293. it('should trigger vm.$set when value is changed via input', function () {
  294. var triggered = false
  295. dir.key = 'foo'
  296. dir.vm = { $set: function (key, val) {
  297. assert.ok(dir.lock, 'the directive should be locked if it has no filters')
  298. assert.strictEqual(key, 'foo')
  299. assert.strictEqual(val, 'bar')
  300. triggered = true
  301. }}
  302. dir.el.value = 'bar'
  303. dir.el.dispatchEvent(mockHTMLEvent('input'))
  304. assert.ok(triggered)
  305. })
  306. it('should remove event listener with unbind()', function () {
  307. var removed = true
  308. dir.vm.$set = function () {
  309. removed = false
  310. }
  311. dir.unbind()
  312. dir.el.dispatchEvent(mockHTMLEvent('input'))
  313. assert.ok(removed)
  314. })
  315. it('should not lock during vm.$set if it has filters', function (done) {
  316. var triggered = false
  317. var dir = mockDirective('model', 'input', 'text')
  318. dir.filters = []
  319. dir.bind()
  320. dir.vm = {$set:function () {
  321. assert.notOk(dir.lock)
  322. triggered = true
  323. }}
  324. dir.el.value = 'foo'
  325. document.body.appendChild(dir.el)
  326. dir.el.dispatchEvent(mockHTMLEvent('input'))
  327. // timeout becuase the update is async
  328. setTimeout(function () {
  329. assert.ok(triggered)
  330. document.body.removeChild(dir.el)
  331. done()
  332. }, 1)
  333. })
  334. after(function () {
  335. document.body.removeChild(dir.el)
  336. })
  337. })
  338. })
  339. describe('if', function () {
  340. it('should remain detached if it was detached during bind() and never attached', function () {
  341. var dir = mockDirective('if')
  342. dir.bind()
  343. dir.update(true)
  344. assert.notOk(dir.el.parentNode)
  345. dir.update(false)
  346. assert.notOk(dir.el.parentNode)
  347. })
  348. it('should remove el and insert ref when value is falsy', function () {
  349. var dir = mockDirective('if'),
  350. parent = document.createElement('div')
  351. parent.appendChild(dir.el)
  352. dir.bind()
  353. dir.update(false)
  354. assert.notOk(dir.el.parentNode)
  355. assert.notOk(parent.contains(dir.el))
  356. // phantomJS weird bug:
  357. // Node.contains() returns false when argument is a comment node.
  358. assert.strictEqual(dir.ref.parentNode, parent)
  359. })
  360. it('should append el and remove ref when value is truthy', function () {
  361. var dir = mockDirective('if'),
  362. parent = document.createElement('div')
  363. parent.appendChild(dir.el)
  364. dir.bind()
  365. dir.update(false)
  366. dir.update(true)
  367. assert.strictEqual(dir.el.parentNode, parent)
  368. assert.ok(parent.contains(dir.el))
  369. assert.notOk(parent.contains(dir.ref))
  370. })
  371. it('should work even if it was detached during bind()', function () {
  372. var dir = mockDirective('if')
  373. dir.bind()
  374. var parent = document.createElement('div')
  375. parent.appendChild(dir.el)
  376. dir.update(false)
  377. assert.strictEqual(dir.parent, parent)
  378. assert.notOk(dir.el.parentNode)
  379. assert.notOk(parent.contains(dir.el))
  380. assert.strictEqual(dir.ref.parentNode, parent)
  381. dir.update(true)
  382. assert.strictEqual(dir.el.parentNode, parent)
  383. assert.ok(parent.contains(dir.el))
  384. assert.notOk(parent.contains(dir.ref))
  385. })
  386. })
  387. describe('on (non-delegated only)', function () {
  388. var dir = mockDirective('on')
  389. dir.arg = 'click'
  390. before(function () {
  391. document.body.appendChild(dir.el)
  392. })
  393. it('should set the handler to be triggered by arg through update()', function () {
  394. var triggered = false
  395. dir.update(function () {
  396. triggered = true
  397. })
  398. dir.el.dispatchEvent(mockMouseEvent('click'))
  399. assert.ok(triggered)
  400. })
  401. it('should wrap the handler to supply expected args', function () {
  402. var vm = dir.binding.compiler.vm, // owner VM
  403. e = mockMouseEvent('click'), // original event
  404. triggered = false
  405. dir.update(function (ev) {
  406. assert.strictEqual(this, vm, 'handler should be called on owner VM')
  407. assert.strictEqual(ev, e, 'event should be passed in')
  408. assert.strictEqual(ev.vm, dir.vm)
  409. triggered = true
  410. })
  411. dir.el.dispatchEvent(e)
  412. assert.ok(triggered)
  413. })
  414. it('should remove previous handler when update() a new handler', function () {
  415. var triggered1 = false,
  416. triggered2 = false
  417. dir.update(function () {
  418. triggered1 = true
  419. })
  420. dir.update(function () {
  421. triggered2 = true
  422. })
  423. dir.el.dispatchEvent(mockMouseEvent('click'))
  424. assert.notOk(triggered1)
  425. assert.ok(triggered2)
  426. })
  427. it('should remove the handler in unbind()', function () {
  428. var triggered = false
  429. dir.update(function () {
  430. triggered = true
  431. })
  432. dir.unbind()
  433. dir.el.dispatchEvent(mockMouseEvent('click'))
  434. assert.notOk(triggered)
  435. assert.strictEqual(dir.handler, null, 'should remove reference to handler')
  436. assert.strictEqual(dir.el.vue_viewmodel, null, 'should remove reference to VM on the element')
  437. })
  438. it('should not use delegation if the event is blur or focus', function () {
  439. var dir = mockDirective('on', 'input'),
  440. triggerCount = 0,
  441. handler = function () {
  442. triggerCount++
  443. }
  444. document.body.appendChild(dir.el)
  445. dir.arg = 'focus'
  446. dir.update(handler)
  447. dir.el.dispatchEvent(mockHTMLEvent('focus'))
  448. assert.strictEqual(triggerCount, 1)
  449. dir.arg = 'blur'
  450. dir.update(handler)
  451. dir.el.dispatchEvent(mockHTMLEvent('blur'))
  452. assert.strictEqual(triggerCount, 2)
  453. document.body.removeChild(dir.el)
  454. })
  455. after(function () {
  456. document.body.removeChild(dir.el)
  457. })
  458. })
  459. describe('pre', function () {
  460. it('should skip compilation', function () {
  461. var testId = 'pre-test'
  462. mock(testId, '<span v-pre><strong>{{lol}}</strong><a v-text="hi"></a></span>')
  463. var t = new Vue({
  464. el: '#' + testId,
  465. data: {
  466. lol: 'heyhey',
  467. hi: 'hohoho'
  468. }
  469. })
  470. assert.strictEqual(t.$el.querySelector('strong').textContent, '{{lol}}')
  471. assert.strictEqual(t.$el.querySelector('a').textContent, '')
  472. assert.ok(t.$el.querySelector('a').hasAttribute('v-text'))
  473. })
  474. })
  475. describe('component', function () {
  476. it('should create a child viewmodel with given constructor', function () {
  477. var testId = 'component-test'
  478. mock(testId, '<div v-component="' + testId + '"></div>')
  479. var t = new Vue({
  480. el: '#' + testId,
  481. data: {
  482. msg: '123'
  483. },
  484. components: {
  485. 'component-test': {
  486. template: '<span>{{msg}}</span>'
  487. }
  488. }
  489. })
  490. assert.strictEqual(t.$el.querySelector('span').textContent, '123')
  491. })
  492. })
  493. describe('with', function () {
  494. it('should create a child viewmodel with given data', function () {
  495. var testId = 'with-test'
  496. mock(testId, '<span v-with="test">{{msg}}</span>')
  497. var t = new Vue({
  498. el: '#' + testId,
  499. data: {
  500. test: {
  501. msg: testId
  502. }
  503. }
  504. })
  505. assert.strictEqual(t.$el.querySelector('span').textContent, testId)
  506. })
  507. })
  508. describe('component-id', function () {
  509. it('should register a VM isntance on its parent\'s $', function () {
  510. var called = false
  511. var Child = Vue.extend({
  512. methods: {
  513. test: function () {
  514. called = true
  515. }
  516. }
  517. })
  518. var t = new Vue({
  519. template: '<div v-component="child" v-component-id="hihi"></div>',
  520. components: {
  521. child: Child
  522. }
  523. })
  524. assert.ok(t.$.hihi instanceof Child)
  525. t.$.hihi.test()
  526. assert.ok(called)
  527. t.$.hihi.$destroy()
  528. assert.notOk('hihi' in t.$)
  529. })
  530. })
  531. // More detailed testing for v-repeat can be found in functional tests.
  532. // this is mainly for code coverage
  533. describe('repeat', function () {
  534. var nextTick = require('vue/src/utils').nextTick,
  535. VM = require('vue/src/viewmodel')
  536. it('should work', function (done) {
  537. var handlerCalled = false
  538. var v = new Vue({
  539. template: '<span v-repeat="items" v-on="click:check">{{title}}</span>',
  540. data: {
  541. items: [
  542. {title: 1},
  543. {title: 2}
  544. ]
  545. },
  546. methods: {
  547. check: function (e) {
  548. assert.ok(e.targetVM instanceof VM)
  549. assert.strictEqual(this, v)
  550. handlerCalled = true
  551. }
  552. }
  553. })
  554. nextTick(function () {
  555. assert.equal(v.$el.innerHTML, '<span>1</span><span>2</span><!--v-repeat-items-->')
  556. v.items.push({title:3})
  557. v.items.pop()
  558. v.items.unshift({title:0})
  559. v.items.shift()
  560. v.items.splice(0, 1, {title:-1})
  561. v.items.sort(function (a, b) {
  562. return a.title > b.title
  563. })
  564. v.items.reverse()
  565. nextTick(function () {
  566. assert.equal(v.$el.innerHTML, '<span>2</span><span>-1</span><!--v-repeat-items-->')
  567. testHandler()
  568. })
  569. })
  570. function testHandler () {
  571. document.getElementById('test').appendChild(v.$el)
  572. var span = v.$el.querySelector('span'),
  573. e = mockMouseEvent('click')
  574. span.dispatchEvent(e)
  575. nextTick(function () {
  576. assert.ok(handlerCalled)
  577. done()
  578. })
  579. }
  580. })
  581. })
  582. describe('style', function () {
  583. it('should apply a normal style', function () {
  584. var d = mockDirective('style')
  585. d.arg = 'text-align'
  586. d.bind()
  587. assert.strictEqual(d.prop, 'textAlign')
  588. d.update('center')
  589. assert.strictEqual(d.el.style.textAlign, 'center')
  590. })
  591. it('should apply prefixed style', function () {
  592. var d = mockDirective('style')
  593. d.arg = '-webkit-transform'
  594. d.bind()
  595. assert.strictEqual(d.prop, 'webkitTransform')
  596. d.update('scale(2)')
  597. assert.strictEqual(d.el.style.webkitTransform, 'scale(2)')
  598. })
  599. it('should auto prefix styles', function () {
  600. var d = mockDirective('style')
  601. d.arg = '$transform'
  602. d.bind()
  603. assert.ok(d.prefixed)
  604. assert.strictEqual(d.prop, 'transform')
  605. var val = 'scale(2)'
  606. d.update(val)
  607. assert.strictEqual(d.el.style.transform, val)
  608. assert.strictEqual(d.el.style.webkitTransform, val)
  609. assert.strictEqual(d.el.style.mozTransform, val)
  610. assert.strictEqual(d.el.style.msTransform, val)
  611. })
  612. })
  613. describe('cloak', function () {
  614. it('should remove itself after the instance is ready', function () {
  615. // it doesn't make sense to test with a mock for this one, so...
  616. var v = new Vue({
  617. template: '<div v-cloak></div>',
  618. replace: true,
  619. ready: function () {
  620. // this hook is attached before the v-cloak hook
  621. // so it should still have the attribute
  622. assert.ok(this.$el.hasAttribute('v-cloak'))
  623. }
  624. })
  625. assert.notOk(v.$el.hasAttribute('v-cloak'))
  626. })
  627. })
  628. })
  629. function mockDirective (dirName, tag, type) {
  630. var dir = Vue.directive(dirName),
  631. ret = {
  632. binding: { compiler: { vm: {} } },
  633. compiler: { vm: {}, options: {}, execHook: function () {}},
  634. el: document.createElement(tag || 'div')
  635. }
  636. if (typeof dir === 'function') {
  637. ret.update = dir
  638. } else {
  639. for (var key in dir) {
  640. ret[key] = dir[key]
  641. }
  642. }
  643. if (tag === 'input') ret.el.type = type || 'text'
  644. return ret
  645. }