directives.js 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771
  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.ownerVM = { $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.ownerVM = {
  186. $set: function () {
  187. removed = false
  188. }
  189. }
  190. dir.unbind()
  191. dir.el.dispatchEvent(mockMouseEvent('click'))
  192. assert.ok(removed)
  193. })
  194. after(function () {
  195. document.body.removeChild(dir.el)
  196. })
  197. })
  198. describe('input[radio]', function () {
  199. var dir1 = mockDirective('model', 'input', 'radio'),
  200. dir2 = mockDirective('model', 'input', 'radio')
  201. dir1.el.name = 'input-radio'
  202. dir2.el.name = 'input-radio'
  203. dir1.el.value = '12345'
  204. dir2.el.value = '54321'
  205. dir1.bind()
  206. dir2.bind()
  207. before(function () {
  208. document.body.appendChild(dir1.el)
  209. document.body.appendChild(dir2.el)
  210. })
  211. it('should set el.checked on update()', function () {
  212. assert.notOk(dir1.el.checked)
  213. dir1.update(12345)
  214. assert.ok(dir1.el.checked)
  215. })
  216. it('should trigger vm.$set when clicked', function () {
  217. var triggered = false
  218. dir2.key = 'radio'
  219. dir2.ownerVM = { $set: function (key, val) {
  220. triggered = true
  221. assert.strictEqual(key, 'radio')
  222. assert.strictEqual(val, dir2.el.value)
  223. }}
  224. dir2.el.dispatchEvent(mockMouseEvent('click'))
  225. assert.ok(triggered)
  226. assert.ok(dir2.el.checked)
  227. assert.notOk(dir1.el.checked)
  228. })
  229. it('should remove listeners on unbind()', function () {
  230. var removed = true
  231. dir1.ownerVM = { $set: function () {
  232. removed = false
  233. }}
  234. dir1.unbind()
  235. dir1.el.dispatchEvent(mockMouseEvent('click'))
  236. assert.ok(removed)
  237. })
  238. after(function () {
  239. document.body.removeChild(dir1.el)
  240. document.body.removeChild(dir2.el)
  241. })
  242. })
  243. describe('select', function () {
  244. var dir = mockDirective('model', 'select'),
  245. o1 = document.createElement('option'),
  246. o2 = document.createElement('option')
  247. o1.value = 0
  248. o2.value = 1
  249. dir.el.appendChild(o1)
  250. dir.el.appendChild(o2)
  251. dir.bind()
  252. before(function () {
  253. document.body.appendChild(dir.el)
  254. })
  255. it('should set value on update()', function () {
  256. dir.update(0)
  257. assert.strictEqual(dir.el.value, '0')
  258. })
  259. it('should trigger vm.$set when value is changed', function () {
  260. var triggered = false
  261. dir.key = 'select'
  262. dir.ownerVM = { $set: function (key, val) {
  263. triggered = true
  264. assert.strictEqual(key, 'select')
  265. assert.equal(val, 1)
  266. }}
  267. dir.el.options.selectedIndex = 1
  268. dir.el.dispatchEvent(mockHTMLEvent('change'))
  269. assert.ok(triggered)
  270. })
  271. it('should remove listener on unbind()', function () {
  272. var removed = true
  273. dir.ownerVM = { $set: function () {
  274. removed = false
  275. }}
  276. dir.unbind()
  277. dir.el.dispatchEvent(mockHTMLEvent('change'))
  278. assert.ok(removed)
  279. })
  280. after(function () {
  281. document.body.removeChild(dir.el)
  282. })
  283. })
  284. describe('input[text] and others', function () {
  285. var dir = mockDirective('model', 'input', 'text')
  286. dir.bind()
  287. before(function () {
  288. document.body.appendChild(dir.el)
  289. })
  290. it('should set the value on update()', function () {
  291. dir.update('foobar')
  292. assert.strictEqual(dir.el.value, 'foobar')
  293. })
  294. // `lazy` option is tested in the API suite
  295. it('should trigger vm.$set when value is changed via input', function () {
  296. var triggered = false
  297. dir.key = 'foo'
  298. dir.ownerVM = { $set: function (key, val) {
  299. assert.ok(dir.lock, 'the directive should be locked if it has no filters')
  300. assert.strictEqual(key, 'foo')
  301. assert.strictEqual(val, 'bar')
  302. triggered = true
  303. }}
  304. dir.el.value = 'bar'
  305. dir.el.dispatchEvent(mockHTMLEvent('input'))
  306. assert.ok(triggered)
  307. })
  308. it('should remove event listener with unbind()', function () {
  309. var removed = true
  310. dir.ownerVM = {
  311. $set: function () {
  312. removed = false
  313. }
  314. }
  315. dir.unbind()
  316. dir.el.dispatchEvent(mockHTMLEvent('input'))
  317. assert.ok(removed)
  318. })
  319. it('should not lock during vm.$set if it has filters', function (done) {
  320. var triggered = false
  321. var dir = mockDirective('model', 'input', 'text')
  322. dir.filters = []
  323. dir.bind()
  324. dir.ownerVM = {$set:function () {
  325. assert.notOk(dir.lock)
  326. triggered = true
  327. }}
  328. dir.el.value = 'foo'
  329. document.body.appendChild(dir.el)
  330. dir.el.dispatchEvent(mockHTMLEvent('input'))
  331. // timeout becuase the update is async
  332. setTimeout(function () {
  333. assert.ok(triggered)
  334. document.body.removeChild(dir.el)
  335. done()
  336. }, 1)
  337. })
  338. after(function () {
  339. document.body.removeChild(dir.el)
  340. })
  341. })
  342. })
  343. describe('if', function () {
  344. it('should remain detached if it was detached during bind() and never attached', function () {
  345. var dir = mockDirective('if')
  346. dir.bind()
  347. dir.update(true)
  348. assert.notOk(dir.el.parentNode)
  349. dir.update(false)
  350. assert.notOk(dir.el.parentNode)
  351. })
  352. it('should remove el and insert ref when value is falsy', function () {
  353. var dir = mockDirective('if'),
  354. parent = document.createElement('div')
  355. parent.appendChild(dir.el)
  356. dir.bind()
  357. dir.update(false)
  358. assert.notOk(dir.el.parentNode)
  359. assert.notOk(parent.contains(dir.el))
  360. // phantomJS weird bug:
  361. // Node.contains() returns false when argument is a comment node.
  362. assert.strictEqual(dir.ref.parentNode, parent)
  363. })
  364. it('should append el and remove ref when value is truthy', function () {
  365. var dir = mockDirective('if'),
  366. parent = document.createElement('div')
  367. parent.appendChild(dir.el)
  368. dir.bind()
  369. dir.update(false)
  370. dir.update(true)
  371. assert.strictEqual(dir.el.parentNode, parent)
  372. assert.ok(parent.contains(dir.el))
  373. assert.notOk(parent.contains(dir.ref))
  374. })
  375. it('should work even if it was detached during bind()', function () {
  376. var dir = mockDirective('if')
  377. dir.bind()
  378. var parent = document.createElement('div')
  379. parent.appendChild(dir.el)
  380. dir.update(false)
  381. assert.strictEqual(dir.parent, parent)
  382. assert.notOk(dir.el.parentNode)
  383. assert.notOk(parent.contains(dir.el))
  384. assert.strictEqual(dir.ref.parentNode, parent)
  385. dir.update(true)
  386. assert.strictEqual(dir.el.parentNode, parent)
  387. assert.ok(parent.contains(dir.el))
  388. assert.notOk(parent.contains(dir.ref))
  389. })
  390. })
  391. describe('on (non-delegated only)', function () {
  392. var dir = mockDirective('on')
  393. dir.arg = 'click'
  394. before(function () {
  395. document.body.appendChild(dir.el)
  396. })
  397. it('should set the handler to be triggered by arg through update()', function () {
  398. var triggered = false
  399. dir.update(function () {
  400. triggered = true
  401. })
  402. dir.el.dispatchEvent(mockMouseEvent('click'))
  403. assert.ok(triggered)
  404. })
  405. it('should wrap the handler to supply expected args', function () {
  406. var vm = dir.binding.compiler.vm, // owner VM
  407. e = mockMouseEvent('click'), // original event
  408. triggered = false
  409. dir.update(function (ev) {
  410. assert.strictEqual(this, vm, 'handler should be called on owner VM')
  411. assert.strictEqual(ev, e, 'event should be passed in')
  412. assert.strictEqual(ev.vm, dir.vm)
  413. triggered = true
  414. })
  415. dir.el.dispatchEvent(e)
  416. assert.ok(triggered)
  417. })
  418. it('should remove previous handler when update() a new handler', function () {
  419. var triggered1 = false,
  420. triggered2 = false
  421. dir.update(function () {
  422. triggered1 = true
  423. })
  424. dir.update(function () {
  425. triggered2 = true
  426. })
  427. dir.el.dispatchEvent(mockMouseEvent('click'))
  428. assert.notOk(triggered1)
  429. assert.ok(triggered2)
  430. })
  431. it('should remove the handler in unbind()', function () {
  432. var triggered = false
  433. dir.update(function () {
  434. triggered = true
  435. })
  436. dir.unbind()
  437. dir.el.dispatchEvent(mockMouseEvent('click'))
  438. assert.notOk(triggered)
  439. assert.strictEqual(dir.handler, null, 'should remove reference to handler')
  440. assert.strictEqual(dir.el.vue_viewmodel, null, 'should remove reference to VM on the element')
  441. })
  442. it('should not use delegation if the event is blur or focus', function () {
  443. var dir = mockDirective('on', 'input'),
  444. triggerCount = 0,
  445. handler = function () {
  446. triggerCount++
  447. }
  448. document.body.appendChild(dir.el)
  449. dir.arg = 'focus'
  450. dir.update(handler)
  451. dir.el.dispatchEvent(mockHTMLEvent('focus'))
  452. assert.strictEqual(triggerCount, 1)
  453. dir.arg = 'blur'
  454. dir.update(handler)
  455. dir.el.dispatchEvent(mockHTMLEvent('blur'))
  456. assert.strictEqual(triggerCount, 2)
  457. document.body.removeChild(dir.el)
  458. })
  459. after(function () {
  460. document.body.removeChild(dir.el)
  461. })
  462. })
  463. describe('pre', function () {
  464. it('should skip compilation', function () {
  465. var testId = 'pre-test'
  466. mock(testId, '<span v-pre><strong>{{lol}}</strong><a v-text="hi"></a></span>')
  467. var t = new Vue({
  468. el: '#' + testId,
  469. data: {
  470. lol: 'heyhey',
  471. hi: 'hohoho'
  472. }
  473. })
  474. assert.strictEqual(t.$el.querySelector('strong').textContent, '{{lol}}')
  475. assert.strictEqual(t.$el.querySelector('a').textContent, '')
  476. assert.ok(t.$el.querySelector('a').hasAttribute('v-text'))
  477. })
  478. })
  479. describe('component', function () {
  480. it('should create a child viewmodel with given constructor', function () {
  481. var testId = 'component-test'
  482. mock(testId, '<div v-component="' + testId + '"></div>')
  483. var t = new Vue({
  484. el: '#' + testId,
  485. data: {
  486. msg: '123'
  487. },
  488. components: {
  489. 'component-test': {
  490. template: '<span>{{msg}}</span>'
  491. }
  492. }
  493. })
  494. assert.strictEqual(t.$el.querySelector('span').textContent, '123')
  495. })
  496. })
  497. describe('with', function () {
  498. it('should create a child viewmodel with given data', function () {
  499. var testId = 'with-test'
  500. mock(testId, '<span v-with="test">{{msg}}</span>')
  501. var t = new Vue({
  502. el: '#' + testId,
  503. data: {
  504. test: {
  505. msg: testId
  506. }
  507. }
  508. })
  509. assert.strictEqual(t.$el.querySelector('span').textContent, testId)
  510. })
  511. })
  512. describe('ref', function () {
  513. it('should register a VM isntance on its parent\'s $', function () {
  514. var called = false
  515. var Child = Vue.extend({
  516. methods: {
  517. test: function () {
  518. called = true
  519. }
  520. }
  521. })
  522. var t = new Vue({
  523. template: '<div v-component="child" v-ref="hihi"></div>',
  524. components: {
  525. child: Child
  526. }
  527. })
  528. assert.ok(t.$.hihi instanceof Child)
  529. t.$.hihi.test()
  530. assert.ok(called)
  531. t.$.hihi.$destroy()
  532. assert.notOk('hihi' in t.$)
  533. })
  534. })
  535. // More detailed testing for v-repeat can be found in functional tests.
  536. // this is mainly for code coverage
  537. describe('repeat', function () {
  538. var nextTick = require('vue/src/utils').nextTick,
  539. VM = require('vue/src/viewmodel')
  540. it('should work', function (done) {
  541. var handlerCalled = false
  542. var v = new Vue({
  543. template: '<span v-repeat="items" v-on="click:check">{{title}}</span>',
  544. data: {
  545. items: [
  546. {title: 1},
  547. {title: 2}
  548. ]
  549. },
  550. methods: {
  551. check: function (e) {
  552. assert.ok(e.targetVM instanceof VM)
  553. assert.strictEqual(this, v)
  554. handlerCalled = true
  555. }
  556. }
  557. })
  558. nextTick(function () {
  559. assert.equal(v.$el.innerHTML, '<span>1</span><span>2</span><!--v-repeat-items-->')
  560. v.items.push({title:3})
  561. v.items.pop()
  562. v.items.unshift({title:0})
  563. v.items.shift()
  564. v.items.splice(0, 1, {title:-1})
  565. v.items.sort(function (a, b) {
  566. return a.title > b.title
  567. })
  568. v.items.reverse()
  569. nextTick(function () {
  570. assert.equal(v.$el.innerHTML, '<span>2</span><span>-1</span><!--v-repeat-items-->')
  571. testHandler()
  572. })
  573. })
  574. function testHandler () {
  575. document.getElementById('test').appendChild(v.$el)
  576. var span = v.$el.querySelector('span'),
  577. e = mockMouseEvent('click')
  578. span.dispatchEvent(e)
  579. nextTick(function () {
  580. assert.ok(handlerCalled)
  581. done()
  582. })
  583. }
  584. })
  585. })
  586. describe('style', function () {
  587. it('should apply a normal style', function () {
  588. var d = mockDirective('style')
  589. d.arg = 'text-align'
  590. d.bind()
  591. assert.strictEqual(d.prop, 'textAlign')
  592. d.update('center')
  593. assert.strictEqual(d.el.style.textAlign, 'center')
  594. })
  595. it('should apply prefixed style', function () {
  596. var d = mockDirective('style')
  597. d.arg = '-webkit-transform'
  598. d.bind()
  599. assert.strictEqual(d.prop, 'webkitTransform')
  600. d.update('scale(2)')
  601. assert.strictEqual(d.el.style.webkitTransform, 'scale(2)')
  602. })
  603. it('should auto prefix styles', function () {
  604. var d = mockDirective('style')
  605. d.arg = '$transform'
  606. d.bind()
  607. assert.ok(d.prefixed)
  608. assert.strictEqual(d.prop, 'transform')
  609. var val = 'scale(2)'
  610. d.update(val)
  611. assert.strictEqual(d.el.style.transform, val)
  612. assert.strictEqual(d.el.style.webkitTransform, val)
  613. assert.strictEqual(d.el.style.mozTransform, val)
  614. assert.strictEqual(d.el.style.msTransform, val)
  615. })
  616. })
  617. describe('cloak', function () {
  618. it('should remove itself after the instance is ready', function () {
  619. // it doesn't make sense to test with a mock for this one, so...
  620. var v = new Vue({
  621. template: '<div v-cloak></div>',
  622. replace: true,
  623. ready: function () {
  624. // this hook is attached before the v-cloak hook
  625. // so it should still have the attribute
  626. assert.ok(this.$el.hasAttribute('v-cloak'))
  627. }
  628. })
  629. assert.notOk(v.$el.hasAttribute('v-cloak'))
  630. })
  631. })
  632. })
  633. function mockDirective (dirName, tag, type) {
  634. var dir = Vue.directive(dirName),
  635. ret = {
  636. binding: { compiler: { vm: {} } },
  637. compiler: { vm: {}, options: {}, execHook: function () {}},
  638. el: document.createElement(tag || 'div')
  639. }
  640. if (typeof dir === 'function') {
  641. ret.update = dir
  642. } else {
  643. for (var key in dir) {
  644. ret[key] = dir[key]
  645. }
  646. }
  647. if (tag === 'input') ret.el.type = type || 'text'
  648. return ret
  649. }