131df24b89fba4493dc6bd5e7a78b5cd4ace7d637918de6d45015811da9d1dccc7c812b574a53f66e26399285fb3bc904c8d274a7cdb7bf1a8fdd955a2034a 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. /* @flow */
  2. /**
  3. * Cross-platform code generation for component v-model
  4. */
  5. export function genComponentModel (
  6. el: ASTElement,
  7. value: string,
  8. modifiers: ?ASTModifiers
  9. ): ?boolean {
  10. const { number, trim } = modifiers || {}
  11. const baseValueExpression = '$$v'
  12. let valueExpression = baseValueExpression
  13. if (trim) {
  14. valueExpression =
  15. `(typeof ${baseValueExpression} === 'string'` +
  16. `? ${baseValueExpression}.trim()` +
  17. `: ${baseValueExpression})`
  18. }
  19. if (number) {
  20. valueExpression = `_n(${valueExpression})`
  21. }
  22. const assignment = genAssignmentCode(value, valueExpression)
  23. el.model = {
  24. value: `(${value})`,
  25. expression: JSON.stringify(value),
  26. callback: `function (${baseValueExpression}) {${assignment}}`
  27. }
  28. }
  29. /**
  30. * Cross-platform codegen helper for generating v-model value assignment code.
  31. */
  32. export function genAssignmentCode (
  33. value: string,
  34. assignment: string
  35. ): string {
  36. const res = parseModel(value)
  37. if (res.key === null) {
  38. return `${value}=${assignment}`
  39. } else {
  40. return `$set(${res.exp}, ${res.key}, ${assignment})`
  41. }
  42. }
  43. /**
  44. * Parse a v-model expression into a base path and a final key segment.
  45. * Handles both dot-path and possible square brackets.
  46. *
  47. * Possible cases:
  48. *
  49. * - test
  50. * - test[key]
  51. * - test[test1[key]]
  52. * - test["a"][key]
  53. * - xxx.test[a[a].test1[key]]
  54. * - test.xxx.a["asa"][test1[key]]
  55. *
  56. */
  57. let len, str, chr, index, expressionPos, expressionEndPos
  58. type ModelParseResult = {
  59. exp: string,
  60. key: string | null
  61. }
  62. export function parseModel (val: string): ModelParseResult {
  63. // Fix https://github.com/vuejs/vue/pull/7730
  64. // allow v-model="obj.val " (trailing whitespace)
  65. val = val.trim()
  66. len = val.length
  67. if (val.indexOf('[') < 0 || val.lastIndexOf(']') < len - 1) {
  68. index = val.lastIndexOf('.')
  69. if (index > -1) {
  70. return {
  71. exp: val.slice(0, index),
  72. key: '"' + val.slice(index + 1) + '"'
  73. }
  74. } else {
  75. return {
  76. exp: val,
  77. key: null
  78. }
  79. }
  80. }
  81. str = val
  82. index = expressionPos = expressionEndPos = 0
  83. while (!eof()) {
  84. chr = next()
  85. /* istanbul ignore if */
  86. if (isStringStart(chr)) {
  87. parseString(chr)
  88. } else if (chr === 0x5B) {
  89. parseBracket(chr)
  90. }
  91. }
  92. return {
  93. exp: val.slice(0, expressionPos),
  94. key: val.slice(expressionPos + 1, expressionEndPos)
  95. }
  96. }
  97. function next (): number {
  98. return str.charCodeAt(++index)
  99. }
  100. function eof (): boolean {
  101. return index >= len
  102. }
  103. function isStringStart (chr: number): boolean {
  104. return chr === 0x22 || chr === 0x27
  105. }
  106. function parseBracket (chr: number): void {
  107. let inBracket = 1
  108. expressionPos = index
  109. while (!eof()) {
  110. chr = next()
  111. if (isStringStart(chr)) {
  112. parseString(chr)
  113. continue
  114. }
  115. if (chr === 0x5B) inBracket++
  116. if (chr === 0x5D) inBracket--
  117. if (inBracket === 0) {
  118. expressionEndPos = index
  119. break
  120. }
  121. }
  122. }
  123. function parseString (chr: number): void {
  124. const stringQuote = chr
  125. while (!eof()) {
  126. chr = next()
  127. if (chr === stringQuote) {
  128. break
  129. }
  130. }
  131. }