model-component.spec.js 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. import Vue from 'vue'
  2. describe('Directive v-model component', () => {
  3. it('should work', done => {
  4. const vm = new Vue({
  5. data: {
  6. msg: 'hello'
  7. },
  8. template: `
  9. <div>
  10. <p>{{ msg }}</p>
  11. <test v-model="msg"></test>
  12. </div>
  13. `,
  14. components: {
  15. test: {
  16. props: ['value'],
  17. template: `<input :value="value" @input="$emit('input', $event.target.value)">`
  18. }
  19. }
  20. }).$mount()
  21. document.body.appendChild(vm.$el)
  22. waitForUpdate(() => {
  23. const input = vm.$el.querySelector('input')
  24. input.value = 'world'
  25. triggerEvent(input, 'input')
  26. }).then(() => {
  27. expect(vm.msg).toEqual('world')
  28. expect(vm.$el.querySelector('p').textContent).toEqual('world')
  29. vm.msg = 'changed'
  30. }).then(() => {
  31. expect(vm.$el.querySelector('p').textContent).toEqual('changed')
  32. expect(vm.$el.querySelector('input').value).toEqual('changed')
  33. }).then(() => {
  34. document.body.removeChild(vm.$el)
  35. }).then(done)
  36. })
  37. it('should work with native tags with "is"', done => {
  38. const vm = new Vue({
  39. data: {
  40. msg: 'hello'
  41. },
  42. template: `
  43. <div>
  44. <p>{{ msg }}</p>
  45. <input is="test" v-model="msg">
  46. </div>
  47. `,
  48. components: {
  49. test: {
  50. props: ['value'],
  51. template: `<input :value="value" @input="$emit('input', $event.target.value)">`
  52. }
  53. }
  54. }).$mount()
  55. document.body.appendChild(vm.$el)
  56. waitForUpdate(() => {
  57. const input = vm.$el.querySelector('input')
  58. input.value = 'world'
  59. triggerEvent(input, 'input')
  60. }).then(() => {
  61. expect(vm.msg).toEqual('world')
  62. expect(vm.$el.querySelector('p').textContent).toEqual('world')
  63. vm.msg = 'changed'
  64. }).then(() => {
  65. expect(vm.$el.querySelector('p').textContent).toEqual('changed')
  66. expect(vm.$el.querySelector('input').value).toEqual('changed')
  67. }).then(() => {
  68. document.body.removeChild(vm.$el)
  69. }).then(done)
  70. })
  71. it('should support customization via model option', done => {
  72. const spy = jasmine.createSpy('update')
  73. const vm = new Vue({
  74. data: {
  75. msg: 'hello'
  76. },
  77. methods: {
  78. spy
  79. },
  80. template: `
  81. <div>
  82. <p>{{ msg }}</p>
  83. <test v-model="msg" @update="spy"></test>
  84. </div>
  85. `,
  86. components: {
  87. test: {
  88. model: {
  89. prop: 'currentValue',
  90. event: 'update'
  91. },
  92. props: ['currentValue'],
  93. template: `<input :value="currentValue" @input="$emit('update', $event.target.value)">`
  94. }
  95. }
  96. }).$mount()
  97. document.body.appendChild(vm.$el)
  98. waitForUpdate(() => {
  99. const input = vm.$el.querySelector('input')
  100. input.value = 'world'
  101. triggerEvent(input, 'input')
  102. }).then(() => {
  103. expect(vm.msg).toEqual('world')
  104. expect(vm.$el.querySelector('p').textContent).toEqual('world')
  105. expect(spy).toHaveBeenCalledWith('world')
  106. vm.msg = 'changed'
  107. }).then(() => {
  108. expect(vm.$el.querySelector('p').textContent).toEqual('changed')
  109. expect(vm.$el.querySelector('input').value).toEqual('changed')
  110. }).then(() => {
  111. document.body.removeChild(vm.$el)
  112. }).then(done)
  113. })
  114. it('modifier: .number', () => {
  115. const vm = new Vue({
  116. template: `<div><my-input ref="input" v-model.number="text"></my-input></div>`,
  117. data: { text: 'foo' },
  118. components: {
  119. 'my-input': {
  120. template: '<input>'
  121. }
  122. }
  123. }).$mount()
  124. expect(vm.text).toBe('foo')
  125. vm.$refs.input.$emit('input', 'bar')
  126. expect(vm.text).toBe('bar')
  127. vm.$refs.input.$emit('input', '123')
  128. expect(vm.text).toBe(123)
  129. })
  130. it('modifier: .trim', () => {
  131. const vm = new Vue({
  132. template: `<div><my-input ref="input" v-model.trim="text"></my-input></div>`,
  133. data: { text: 'foo' },
  134. components: {
  135. 'my-input': {
  136. template: '<input>'
  137. }
  138. }
  139. }).$mount()
  140. expect(vm.text).toBe('foo')
  141. vm.$refs.input.$emit('input', ' bar ')
  142. expect(vm.text).toBe('bar')
  143. vm.$refs.input.$emit('input', ' foo o ')
  144. expect(vm.text).toBe('foo o')
  145. })
  146. // #8436
  147. it('should not double transform mode props', () => {
  148. const BaseInput = {
  149. props: ['value'],
  150. render (h) {
  151. return h('input', {
  152. domProps: {
  153. value: this.value
  154. },
  155. on: {
  156. input: e => this.$emit('input', e.target.value)
  157. }
  158. })
  159. }
  160. }
  161. const FunctionalWrapper = {
  162. functional: true,
  163. render (h, ctx) {
  164. return h(BaseInput, ctx.data)
  165. }
  166. }
  167. let triggerCount = 0
  168. const vm = new Vue({
  169. components: {
  170. FunctionalWrapper
  171. },
  172. template: `
  173. <div>
  174. <functional-wrapper v-model="val"/>
  175. </div>
  176. `,
  177. data: {
  178. internalVal: ''
  179. },
  180. computed: {
  181. val: {
  182. get () {
  183. return this.internalVal
  184. },
  185. set (val) {
  186. triggerCount++
  187. this.internalVal = val
  188. }
  189. }
  190. }
  191. }).$mount()
  192. document.body.appendChild(vm.$el)
  193. triggerEvent(vm.$el.querySelector('input'), 'input')
  194. expect(triggerCount).toBe(1)
  195. document.body.removeChild(vm.$el)
  196. })
  197. // #9330
  198. it('should add value to $attrs if not defined in props', () => {
  199. const TestComponent = {
  200. inheritAttrs: false,
  201. render (h) {
  202. return h('div', this.$attrs.value)
  203. }
  204. }
  205. const vm = new Vue({
  206. components: {
  207. TestComponent
  208. },
  209. template: `
  210. <div>
  211. <test-component v-model="val"/>
  212. </div>
  213. `,
  214. data: {
  215. val: 'foo'
  216. }
  217. }).$mount()
  218. expect(vm.$el.innerHTML).toBe('<div>foo</div>');
  219. })
  220. })