if_spec.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418
  1. var _ = require('../../../../../src/util')
  2. var Vue = require('../../../../../src/vue')
  3. if (_.inBrowser) {
  4. describe('v-if', function () {
  5. var el
  6. beforeEach(function () {
  7. el = document.createElement('div')
  8. spyOn(_, 'warn')
  9. })
  10. it('normal', function (done) {
  11. var vm = new Vue({
  12. el: el,
  13. data: { test: false, a: 'A' },
  14. template: '<div v-if="test"><test :a="a"></test></div>',
  15. components: {
  16. test: {
  17. props: ['a'],
  18. template: '{{a}}'
  19. }
  20. }
  21. })
  22. // lazy instantitation
  23. expect(el.innerHTML).toBe('')
  24. expect(vm.$children.length).toBe(0)
  25. vm.test = true
  26. _.nextTick(function () {
  27. expect(el.innerHTML).toBe('<div><test>A</test></div>')
  28. expect(vm.$children.length).toBe(1)
  29. vm.test = false
  30. _.nextTick(function () {
  31. expect(el.innerHTML).toBe('')
  32. expect(vm.$children.length).toBe(0)
  33. vm.test = true
  34. _.nextTick(function () {
  35. expect(el.innerHTML).toBe('<div><test>A</test></div>')
  36. expect(vm.$children.length).toBe(1)
  37. var child = vm.$children[0]
  38. vm.$destroy()
  39. expect(child._isDestroyed).toBe(true)
  40. done()
  41. })
  42. })
  43. })
  44. })
  45. it('template block', function (done) {
  46. var vm = new Vue({
  47. el: el,
  48. data: { test: false, a: 'A', b: 'B' },
  49. template: '<template v-if="test"><p>{{a}}</p><p>{{b}}</p></template>'
  50. })
  51. // lazy instantitation
  52. expect(el.innerHTML).toBe('')
  53. vm.test = true
  54. _.nextTick(function () {
  55. expect(el.innerHTML).toBe('<p>A</p><p>B</p>')
  56. vm.test = false
  57. _.nextTick(function () {
  58. expect(el.innerHTML).toBe('')
  59. done()
  60. })
  61. })
  62. })
  63. it('v-if + component', function (done) {
  64. var attachSpy = jasmine.createSpy()
  65. var detachSpy = jasmine.createSpy()
  66. var readySpy = jasmine.createSpy()
  67. var vm = new Vue({
  68. el: el,
  69. data: { ok: false },
  70. template: '<test v-if="ok"></test>',
  71. components: {
  72. test: {
  73. data: function () {
  74. return { a: 123 }
  75. },
  76. template: '{{a}}',
  77. ready: readySpy,
  78. attached: attachSpy,
  79. detached: detachSpy
  80. }
  81. }
  82. })
  83. vm.$appendTo(document.body)
  84. expect(el.innerHTML).toBe('')
  85. expect(vm.$children.length).toBe(0)
  86. vm.ok = true
  87. _.nextTick(function () {
  88. expect(el.innerHTML).toBe('<test>123</test>')
  89. expect(vm.$children.length).toBe(1)
  90. expect(attachSpy).toHaveBeenCalled()
  91. expect(readySpy).toHaveBeenCalled()
  92. vm.ok = false
  93. _.nextTick(function () {
  94. expect(detachSpy).toHaveBeenCalled()
  95. expect(el.innerHTML).toBe('')
  96. expect(vm.$children.length).toBe(0)
  97. vm.$remove()
  98. done()
  99. })
  100. })
  101. })
  102. it('v-if + dynamic component', function (done) {
  103. var vm = new Vue({
  104. el: el,
  105. data: {
  106. ok: false,
  107. view: 'view-a'
  108. },
  109. template: '<component :is="view" v-if="ok"></component>',
  110. components: {
  111. 'view-a': {
  112. template: 'AAA'
  113. },
  114. 'view-b': {
  115. template: 'BBB'
  116. }
  117. }
  118. })
  119. expect(el.innerHTML).toBe('')
  120. expect(vm.$children.length).toBe(0)
  121. // toggle if with lazy instantiation
  122. vm.ok = true
  123. _.nextTick(function () {
  124. expect(el.innerHTML).toBe('<component>AAA</component>')
  125. expect(vm.$children.length).toBe(1)
  126. // switch view when if=true
  127. vm.view = 'view-b'
  128. _.nextTick(function () {
  129. expect(el.innerHTML).toBe('<component>BBB</component>')
  130. expect(vm.$children.length).toBe(1)
  131. // toggle if when already instantiated
  132. vm.ok = false
  133. _.nextTick(function () {
  134. expect(el.innerHTML).toBe('')
  135. expect(vm.$children.length).toBe(0)
  136. // toggle if and switch view at the same time
  137. vm.view = 'view-a'
  138. vm.ok = true
  139. _.nextTick(function () {
  140. expect(el.innerHTML).toBe('<component>AAA</component>')
  141. expect(vm.$children.length).toBe(1)
  142. done()
  143. })
  144. })
  145. })
  146. })
  147. })
  148. it('v-if with different truthy values', function (done) {
  149. var vm = new Vue({
  150. el: el,
  151. data: {
  152. a: 1
  153. },
  154. template: '<div v-if="a">{{a}}</div>'
  155. })
  156. expect(el.innerHTML).toBe('<div>1</div>')
  157. vm.a = 2
  158. _.nextTick(function () {
  159. expect(el.innerHTML).toBe('<div>2</div>')
  160. done()
  161. })
  162. })
  163. it('invalid warn', function () {
  164. el.setAttribute('v-if', 'test')
  165. new Vue({
  166. el: el
  167. })
  168. expect(hasWarned(_, 'cannot be used on an instance root element')).toBe(true)
  169. })
  170. it('call attach/detach for transcluded components', function (done) {
  171. document.body.appendChild(el)
  172. var attachSpy = jasmine.createSpy('attached')
  173. var detachSpy = jasmine.createSpy('detached')
  174. var vm = new Vue({
  175. el: el,
  176. data: { show: true },
  177. template: '<outer><transcluded></transcluded></outer>',
  178. components: {
  179. outer: {
  180. template: '<div v-if="$parent.show"><slot></slot></div>'
  181. },
  182. transcluded: {
  183. template: 'transcluded',
  184. attached: attachSpy,
  185. detached: detachSpy
  186. }
  187. }
  188. })
  189. expect(attachSpy).toHaveBeenCalled()
  190. vm.show = false
  191. _.nextTick(function () {
  192. expect(detachSpy).toHaveBeenCalled()
  193. document.body.removeChild(el)
  194. done()
  195. })
  196. })
  197. it('call attach/detach for dynamicly created components inside if block', function (done) {
  198. document.body.appendChild(el)
  199. var attachSpy = jasmine.createSpy('attached')
  200. var detachSpy = jasmine.createSpy('detached')
  201. var transcluded = {
  202. props: ['a'],
  203. template: '{{a}}',
  204. attached: attachSpy,
  205. detached: detachSpy
  206. }
  207. var vm = new Vue({
  208. el: el,
  209. data: {
  210. show: true,
  211. list: [{a: 0}]
  212. },
  213. template:
  214. '<outer>' +
  215. '<div>' + // an extra layer to test components deep inside the tree
  216. '<transcluded v-for="item in list" :a="item.a"></transcluded>' +
  217. '</div>' +
  218. '</outer>',
  219. components: {
  220. outer: {
  221. template:
  222. '<div v-if="$parent.show">' +
  223. '<slot></slot>' +
  224. '</div>' +
  225. // this is to test that compnents that are not in the if block
  226. // should not fire attach/detach when v-if toggles
  227. '<transcluded></transcluded>',
  228. components: {
  229. transcluded: transcluded
  230. }
  231. },
  232. transcluded: transcluded
  233. }
  234. })
  235. assertMarkup()
  236. expect(attachSpy.calls.count()).toBe(2)
  237. vm.show = false
  238. _.nextTick(function () {
  239. assertMarkup()
  240. expect(detachSpy.calls.count()).toBe(1)
  241. vm.list.push({a: 1})
  242. vm.show = true
  243. _.nextTick(function () {
  244. assertMarkup()
  245. expect(attachSpy.calls.count()).toBe(2 + 2)
  246. vm.list.push({a: 2})
  247. vm.show = false
  248. _.nextTick(function () {
  249. assertMarkup()
  250. expect(attachSpy.calls.count()).toBe(2 + 2 + 1)
  251. expect(detachSpy.calls.count()).toBe(1 + 3)
  252. document.body.removeChild(el)
  253. done()
  254. })
  255. })
  256. })
  257. function assertMarkup () {
  258. var showBlock = vm.show
  259. ? '<div><div>' +
  260. vm.list.map(function (o) {
  261. return '<transcluded>' + o.a + '</transcluded>'
  262. }).join('') +
  263. '</div></div>'
  264. : ''
  265. var markup =
  266. '<outer>' +
  267. showBlock +
  268. '<transcluded></transcluded>' +
  269. '</outer>'
  270. expect(el.innerHTML).toBe(markup)
  271. }
  272. })
  273. it('call attach/detach for nested ifs', function (done) {
  274. var attachSpy = jasmine.createSpy('attached')
  275. var detachSpy = jasmine.createSpy('detached')
  276. document.body.appendChild(el)
  277. var vm = new Vue({
  278. el: el,
  279. data: {
  280. showOuter: true,
  281. showInner: false
  282. },
  283. template:
  284. '<div v-if="showOuter">' +
  285. '<div v-if="showInner">' +
  286. '<test></test>' +
  287. '</div>' +
  288. '</div>',
  289. components: {
  290. test: {
  291. attached: attachSpy,
  292. detached: detachSpy
  293. }
  294. }
  295. })
  296. expect(attachSpy).not.toHaveBeenCalled()
  297. vm.showInner = true
  298. _.nextTick(function () {
  299. expect(attachSpy.calls.count()).toBe(1)
  300. vm.showOuter = false
  301. _.nextTick(function () {
  302. expect(detachSpy.calls.count()).toBe(1)
  303. document.body.removeChild(el)
  304. done()
  305. })
  306. })
  307. })
  308. // #893 in IE textNodes do not have `contains` method
  309. it('call attach/detach: comparing textNodes in IE', function (done) {
  310. document.body.appendChild(el)
  311. var attachSpy = jasmine.createSpy('attached')
  312. var detachSpy = jasmine.createSpy('detached')
  313. var vm = new Vue({
  314. el: el,
  315. data: {
  316. show: true
  317. },
  318. template: '<template v-if="show"><test></test></template>',
  319. components: {
  320. test: {
  321. template: 'hi',
  322. replace: true,
  323. attached: attachSpy,
  324. detached: detachSpy
  325. }
  326. }
  327. })
  328. assertMarkup()
  329. assertCalls(1, 0)
  330. vm.show = false
  331. _.nextTick(function () {
  332. assertMarkup()
  333. assertCalls(1, 1)
  334. vm.show = true
  335. _.nextTick(function () {
  336. assertMarkup()
  337. assertCalls(2, 1)
  338. vm.show = false
  339. _.nextTick(function () {
  340. assertMarkup()
  341. assertCalls(2, 2)
  342. document.body.removeChild(el)
  343. done()
  344. })
  345. })
  346. })
  347. function assertMarkup () {
  348. expect(el.innerHTML).toBe(vm.show ? 'hi' : '')
  349. }
  350. function assertCalls (attach, detach) {
  351. expect(attachSpy.calls.count()).toBe(attach)
  352. expect(detachSpy.calls.count()).toBe(detach)
  353. }
  354. })
  355. // #1097 v-if components not having correct parent
  356. it('compile with correct transclusion host', function () {
  357. var parentA
  358. var parentB
  359. new Vue({
  360. el: el,
  361. data: {
  362. show: true
  363. },
  364. template: '<parent><child v-if="show"></child></parent>',
  365. components: {
  366. parent: {
  367. template: '<slot></slot>',
  368. created: function () {
  369. parentA = this
  370. }
  371. },
  372. child: {
  373. created: function () {
  374. parentB = this.$parent
  375. }
  376. }
  377. }
  378. })
  379. expect(parentA).toBeTruthy()
  380. expect(parentA).toBe(parentB)
  381. })
  382. it('if + else', function (done) {
  383. var vm = new Vue({
  384. el: el,
  385. data: { test: false, a: 'A', b: 'B' },
  386. template: '<div v-if="test">{{a}}</div><div v-else>{{b}}</div>'
  387. })
  388. expect(el.textContent).toBe('B')
  389. vm.test = true
  390. _.nextTick(function () {
  391. expect(el.textContent).toBe('A')
  392. vm.test = false
  393. _.nextTick(function () {
  394. expect(el.textContent).toBe('B')
  395. done()
  396. })
  397. })
  398. })
  399. })
  400. }