Skip to content

Commit 4f84aef

Browse files
Jinjiangyyx990803
authored andcommitted
support v-model in weex (vuejs#4178)
* [wip] supported v-model in Weex * fixed v-model in weex * added test cases for v-model in weex * used strToRegExp for all test cases in weex * fixed eslint * fixed parseModel test case
1 parent 69eab86 commit 4f84aef

File tree

10 files changed

+210
-106
lines changed

10 files changed

+210
-106
lines changed

src/compiler/helpers.js

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,3 +94,87 @@ export function getAndRemoveAttr (el: ASTElement, name: string): ?string {
9494
}
9595
return val
9696
}
97+
98+
let len, str, chr, index, expressionPos, expressionEndPos
99+
100+
/**
101+
* parse directive model to do the array update transform. a[idx] = val => $$a.splice($$idx, 1, val)
102+
*
103+
* for loop possible cases:
104+
*
105+
* - test
106+
* - test[idx]
107+
* - test[test1[idx]]
108+
* - test["a"][idx]
109+
* - xxx.test[a[a].test1[idx]]
110+
* - test.xxx.a["asa"][test1[idx]]
111+
*
112+
*/
113+
114+
export function parseModel (val: string): Object {
115+
str = val
116+
len = str.length
117+
index = expressionPos = expressionEndPos = 0
118+
119+
if (val.indexOf('[') < 0) {
120+
return {
121+
exp: val,
122+
idx: null
123+
}
124+
}
125+
126+
while (!eof()) {
127+
chr = next()
128+
/* istanbul ignore if */
129+
if (isStringStart(chr)) {
130+
parseString(chr)
131+
} else if (chr === 0x5B) {
132+
parseBracket(chr)
133+
}
134+
}
135+
136+
return {
137+
exp: val.substring(0, expressionPos),
138+
idx: val.substring(expressionPos + 1, expressionEndPos)
139+
}
140+
}
141+
142+
function next (): number {
143+
return str.charCodeAt(++index)
144+
}
145+
146+
function eof (): boolean {
147+
return index >= len
148+
}
149+
150+
function isStringStart (chr: number): boolean {
151+
return chr === 0x22 || chr === 0x27
152+
}
153+
154+
function parseBracket (chr: number): void {
155+
let inBracket = 1
156+
expressionPos = index
157+
while (!eof()) {
158+
chr = next()
159+
if (isStringStart(chr)) {
160+
parseString(chr)
161+
continue
162+
}
163+
if (chr === 0x5B) inBracket++
164+
if (chr === 0x5D) inBracket--
165+
if (inBracket === 0) {
166+
expressionEndPos = index
167+
break
168+
}
169+
}
170+
}
171+
172+
function parseString (chr: number): void {
173+
const stringQuote = chr
174+
while (!eof()) {
175+
chr = next()
176+
if (chr === stringQuote) {
177+
break
178+
}
179+
}
180+
}

src/platforms/web/compiler/directives/model.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
/* @flow */
22

33
import { isIE } from 'core/util/env'
4-
import { addHandler, addProp, getBindingAttr } from 'compiler/helpers'
5-
import parseModel from 'web/util/model'
4+
import { addHandler, addProp, getBindingAttr, parseModel } from 'compiler/helpers'
65

76
let warn
87

src/platforms/web/util/model.js

Lines changed: 0 additions & 85 deletions
This file was deleted.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,5 @@
1+
import model from './model'
2+
13
export default {
4+
model
25
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/* @flow */
2+
3+
import { addHandler, addAttr, parseModel } from 'compiler/helpers'
4+
5+
export default function model (
6+
el: ASTElement,
7+
dir: ASTDirective,
8+
_warn: Function
9+
): ?boolean {
10+
genDefaultModel(el, dir.value, dir.modifiers)
11+
}
12+
13+
function genDefaultModel (
14+
el: ASTElement,
15+
value: string,
16+
modifiers: ?ASTModifiers
17+
): ?boolean {
18+
const { lazy, trim } = modifiers || {}
19+
const event = lazy ? 'change' : 'input'
20+
const isNative = el.tag === 'input' || el.tag === 'textarea'
21+
const valueExpression = isNative
22+
? `$event.target.attr.value${trim ? '.trim()' : ''}`
23+
: `$event`
24+
const code = genAssignmentCode(value, valueExpression)
25+
addAttr(el, 'value', `(${value})`)
26+
addHandler(el, event, code, null, true)
27+
}
28+
29+
function genAssignmentCode (value: string, assignment: string): string {
30+
const modelRs = parseModel(value)
31+
if (modelRs.idx === null) {
32+
return `${value}=${assignment}`
33+
} else {
34+
return `var $$exp = ${modelRs.exp}, $$idx = ${modelRs.idx};` +
35+
`if (!Array.isArray($$exp)){` +
36+
`${value}=${assignment}}` +
37+
`else{$$exp.splice($$idx, 1, ${assignment})}`
38+
}
39+
}

test/unit/features/directives/model-parse.spec.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import parseModel from 'web/util/model'
1+
import { parseModel } from 'compiler/helpers'
22

33
describe('model expression parser', () => {
44
it('parse string in brackets', () => {

test/weex/compiler/class.spec.js

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,56 @@
11
import { compile } from '../../../packages/weex-template-compiler'
2+
import { strToRegExp } from '../helpers/index'
23

34
describe('compile class', () => {
45
it('should be compiled', () => {
56
const { render, staticRenderFns, errors } = compile(`<div class="a b c"></div>`)
67
expect(render).not.toBeUndefined()
78
expect(staticRenderFns).not.toBeUndefined()
89
expect(staticRenderFns.length).toEqual(1)
9-
expect(staticRenderFns).toMatch(/staticClass\:\["a","b","c"\]/)
10+
expect(staticRenderFns).toMatch(strToRegExp(`staticClass:["a","b","c"]`))
1011
expect(errors).toEqual([])
1112
})
1213

1314
it('should compile dynamic class', () => {
1415
const { render, staticRenderFns, errors } = compile(`<div class="a {{b}} c"></div>`)
1516
expect(render).not.toBeUndefined()
1617
expect(staticRenderFns).toEqual([])
17-
expect(render).toMatch(/class\:\["a",_s\(b\),"c"\]/)
18+
expect(render).toMatch(strToRegExp(`class:["a",_s(b),"c"]`))
1819
expect(errors).not.toBeUndefined()
1920
expect(errors.length).toEqual(1)
20-
expect(errors[0]).toMatch(/a \{\{b\}\} c/)
21-
expect(errors[0]).toMatch(/v\-bind/)
21+
expect(errors[0]).toMatch(strToRegExp(`a {{b}} c`))
22+
expect(errors[0]).toMatch(strToRegExp(`v-bind`))
2223
})
2324

2425
it('should compile class binding of array', () => {
2526
const { render, staticRenderFns, errors } = compile(`<div v-bind:class="['a', 'b', c]"></div>`)
2627
expect(render).not.toBeUndefined()
2728
expect(staticRenderFns).toEqual([])
28-
expect(render).toMatch(/class\:\['a', 'b', c\]/)
29+
expect(render).toMatch(strToRegExp(`class:['a', 'b', c]`))
2930
expect(errors).toEqual([])
3031
})
3132

3233
it('should compile class binding of map', () => {
3334
const { render, staticRenderFns, errors } = compile(`<div v-bind:class="{ a: true, b: x }"></div>`)
3435
expect(render).not.toBeUndefined()
3536
expect(staticRenderFns).toEqual([])
36-
expect(render).toMatch(/class\:\{ a\: true, b\: x \}/)
37+
expect(render).toMatch(strToRegExp(`class:{ a: true, b: x }`))
3738
expect(errors).toEqual([])
3839
})
3940

4041
it('should compile class binding of a variable', () => {
4142
const { render, staticRenderFns, errors } = compile(`<div v-bind:class="x"></div>`)
4243
expect(render).not.toBeUndefined()
4344
expect(staticRenderFns).toEqual([])
44-
expect(render).toMatch(/class\:x/)
45+
expect(render).toMatch(strToRegExp(`class:x`))
4546
expect(errors).toEqual([])
4647
})
4748

4849
it('should compile class binding by shorthand', () => {
4950
const { render, staticRenderFns, errors } = compile(`<div :class="['a', 'b', c]"></div>`)
5051
expect(render).not.toBeUndefined()
5152
expect(staticRenderFns).toEqual([])
52-
expect(render).toMatch(/class\:\['a', 'b', c\]/)
53+
expect(render).toMatch(strToRegExp(`class:['a', 'b', c]`))
5354
expect(errors).toEqual([])
5455
})
5556
})

test/weex/compiler/style.spec.js

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
import { compile } from '../../../packages/weex-template-compiler'
2+
import { strToRegExp } from '../helpers/index'
23

34
describe('compile style', () => {
45
it('should be compiled', () => {
56
const { render, staticRenderFns, errors } = compile(`<div style="a: x; b: y"></div>`)
67
expect(render).not.toBeUndefined()
78
expect(staticRenderFns).not.toBeUndefined()
89
expect(staticRenderFns.length).toEqual(1)
9-
expect(staticRenderFns).toMatch(/staticStyle\:\{a:"x",b:"y"\}/)
10+
expect(staticRenderFns).toMatch(strToRegExp(`staticStyle:{a:"x",b:"y"}`))
1011
expect(errors).toEqual([])
1112
})
1213

@@ -24,7 +25,7 @@ describe('compile style', () => {
2425
expect(render).not.toBeUndefined()
2526
expect(staticRenderFns).not.toBeUndefined()
2627
expect(staticRenderFns.length).toEqual(1)
27-
expect(staticRenderFns).toMatch(/staticStyle\:\{a:"x",b:"y"\}/)
28+
expect(staticRenderFns).toMatch(strToRegExp(`staticStyle:{a:"x",b:"y"}`))
2829
expect(errors).toEqual([])
2930
})
3031

@@ -33,50 +34,50 @@ describe('compile style', () => {
3334
expect(render).not.toBeUndefined()
3435
expect(staticRenderFns).not.toBeUndefined()
3536
expect(staticRenderFns.length).toEqual(1)
36-
expect(staticRenderFns).toMatch(/staticStyle\:\{AbcDef\:"x-y",abcDef\:"x-y"\}/)
37+
expect(staticRenderFns).toMatch(strToRegExp(`staticStyle:{AbcDef:"x-y",abcDef:"x-y"}`))
3738
expect(errors).toEqual([])
3839
})
3940

4041
it('should compile dynamic style', () => {
4142
const { render, staticRenderFns, errors } = compile(`<div style="a: x; b: {{y}}"></div>`)
4243
expect(render).not.toBeUndefined()
4344
expect(staticRenderFns).toEqual([])
44-
expect(render).toMatch(/style\:\{a:"x",b:_s\(y\)\}/)
45+
expect(render).toMatch(strToRegExp(`style:{a:"x",b:_s(y)}`))
4546
expect(errors).not.toBeUndefined()
4647
expect(errors.length).toEqual(1)
47-
expect(errors[0]).toMatch(/b\: \{\{y\}\}/)
48-
expect(errors[0]).toMatch(/v\-bind/)
48+
expect(errors[0]).toMatch(strToRegExp(`b: {{y}}`))
49+
expect(errors[0]).toMatch(strToRegExp(`v-bind`))
4950
})
5051

5152
it('should compile style binding of array', () => {
5253
const { render, staticRenderFns, errors } = compile(`<div v-bind:style="[a, b, c]"></div>`)
5354
expect(render).not.toBeUndefined()
5455
expect(staticRenderFns).toEqual([])
55-
expect(render).toMatch(/style\:\[a, b, c\]/)
56+
expect(render).toMatch(strToRegExp(`style:[a, b, c]`))
5657
expect(errors).toEqual([])
5758
})
5859

5960
it('should compile style binding of map', () => {
6061
const { render, staticRenderFns, errors } = compile(`<div v-bind:style="{ a: x, b: 'y' + z }"></div>`)
6162
expect(render).not.toBeUndefined()
6263
expect(staticRenderFns).toEqual([])
63-
expect(render).toMatch(/style\:\{ a\: x, b\: 'y' \+ z \}/)
64+
expect(render).toMatch(strToRegExp(`style:{ a: x, b: 'y' + z }`))
6465
expect(errors).toEqual([])
6566
})
6667

6768
it('should compile style binding of a variable', () => {
6869
const { render, staticRenderFns, errors } = compile(`<div v-bind:style="x"></div>`)
6970
expect(render).not.toBeUndefined()
7071
expect(staticRenderFns).toEqual([])
71-
expect(render).toMatch(/style\:x/)
72+
expect(render).toMatch(strToRegExp(`style:x`))
7273
expect(errors).toEqual([])
7374
})
7475

7576
it('should compile style binding by shorthand', () => {
7677
const { render, staticRenderFns, errors } = compile(`<div :style="[a, b, c]"></div>`)
7778
expect(render).not.toBeUndefined()
7879
expect(staticRenderFns).toEqual([])
79-
expect(render).toMatch(/style\:\[a, b, c\]/)
80+
expect(render).toMatch(strToRegExp(`style:[a, b, c]`))
8081
expect(errors).toEqual([])
8182
})
8283
})

0 commit comments

Comments
 (0)