Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

Commit 5001c1a

Browse files
committed
refactor($interpolate): improve interpolation service add documentation
1 parent 0f6b2ef commit 5001c1a

File tree

3 files changed

+226
-157
lines changed

3 files changed

+226
-157
lines changed

src/service/interpolate.js

Lines changed: 134 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -1,82 +1,145 @@
11
'use strict';
22

3-
function $InterpolateProvider(){
4-
this.$get = ['$parse', function($parse){
5-
return function(text, templateOnly) {
6-
var bindings = parseBindings(text);
7-
if (hasBindings(bindings) || !templateOnly) {
8-
return compileBindTemplate(text);
9-
}
10-
};
11-
}];
12-
}
3+
/**
4+
* @ngdoc function
5+
* @name angular.module.ng.$interpolateProvider
6+
* @function
7+
*
8+
* @description
9+
*
10+
* Used for configuring the interpolation markup. Deafults to `{{` and `}}`.
11+
*/
12+
function $InterpolateProvider() {
13+
var startSymbol = '{{';
14+
var endSymbol = '}}';
1315

14-
var bindTemplateCache = {};
15-
function compileBindTemplate(template){
16-
var fn = bindTemplateCache[template];
17-
if (!fn) {
18-
var bindings = [];
19-
forEach(parseBindings(template), function(text){
20-
var exp = binding(text);
21-
bindings.push(exp
22-
? function(scope, element) { return scope.$eval(exp); }
23-
: function() { return text; });
24-
});
25-
bindTemplateCache[template] = fn = function(scope, element, prettyPrintJson) {
26-
var parts = [],
27-
hadOwnElement = scope.hasOwnProperty('$element'),
28-
oldElement = scope.$element;
16+
/**
17+
* @ngdoc method
18+
* @name angular.module.ng.$interpolateProvider#startSymbol
19+
* @methodOf angular.module.ng.$interpolateProvider
20+
* @description
21+
* Symbol to denote start of expression in the interpolated string. Defaults to `{{`.
22+
*
23+
* @prop {string=} value new value to set the starting symbol to.
24+
*/
25+
this.startSymbol = function(value){
26+
if (value) {
27+
startSymbol = value;
28+
return this;
29+
} else {
30+
return startSymbol;
31+
}
32+
};
2933

30-
// TODO(misko): get rid of $element
31-
scope.$element = element;
32-
try {
33-
for (var i = 0; i < bindings.length; i++) {
34-
var value = bindings[i](scope, element);
35-
if (isElement(value))
36-
value = '';
37-
else if (isObject(value))
38-
value = toJson(value, prettyPrintJson);
39-
parts.push(value);
40-
}
41-
return parts.join('');
42-
} finally {
43-
if (hadOwnElement) {
44-
scope.$element = oldElement;
45-
} else {
46-
delete scope.$element;
47-
}
48-
}
49-
};
50-
}
51-
return fn;
52-
}
34+
/**
35+
* @ngdoc method
36+
* @name angular.module.ng.$interpolateProvider#endSymbol
37+
* @methodOf angular.module.ng.$interpolateProvider
38+
* @description
39+
* Symbol to denote the end of expression in the interpolated string. Defaults to `}}`.
40+
*
41+
* @prop {string=} value new value to set the ending symbol to.
42+
*/
43+
this.endSymbol = function(value){
44+
if (value) {
45+
endSymbol = value;
46+
return this;
47+
} else {
48+
return startSymbol;
49+
}
50+
};
5351

5452

55-
function parseBindings(string) {
56-
var results = [];
57-
var lastIndex = 0;
58-
var index;
59-
while((index = string.indexOf('{{', lastIndex)) > -1) {
60-
if (lastIndex < index)
61-
results.push(string.substr(lastIndex, index - lastIndex));
62-
lastIndex = index;
53+
this.$get = ['$parse', function($parse) {
54+
var startSymbolLength = startSymbol.length,
55+
endSymbolLength = endSymbol.length;
6356

64-
index = string.indexOf('}}', index);
65-
index = index < 0 ? string.length : index + 2;
57+
/**
58+
* @ngdoc function
59+
* @name angular.module.ng.$interpolate
60+
* @function
61+
*
62+
* @requires $parse
63+
*
64+
* @description
65+
*
66+
* Compiles a string with markup into an interpolation function. This service is used by the
67+
* HTML {@link angular.module.ng.$compile $compile} service for data binding. See
68+
* {@link angular.module.ng.$interpolateProvider $interpolateProvider} for configuring the
69+
* interpolation markup.
70+
*
71+
*
72+
<pre>
73+
var $interpolate = ...; // injected
74+
var exp = $interpolate('Hello {{name}}!');
75+
expect(exp({name:'Angular'}).toEqual('Hello Angular!');
76+
</pre>
77+
*
78+
*
79+
* @param {string} text The text with markup to interpolate.
80+
* @param {boolean=} mustHaveExpression if set to true then the interpolation string must have
81+
* embedded expression in order to return an interpolation function. Strings with no
82+
* embedded expression will return null for the interpolation function.
83+
* @returns {function(context)} an interpolation function which is used to compute the interpolated
84+
* string. The function has these parameters:
85+
*
86+
* * `context`: an object against which any expressions embedded in the strings are evaluated
87+
* against.
88+
*
89+
*/
90+
return function(text, mustHaveExpression) {
91+
var startIndex,
92+
endIndex,
93+
index = 0,
94+
parts = [],
95+
length = text.length,
96+
hasInterpolation = false,
97+
fn,
98+
exp,
99+
concat = [];
66100

67-
results.push(string.substr(lastIndex, index - lastIndex));
68-
lastIndex = index;
69-
}
70-
if (lastIndex != string.length)
71-
results.push(string.substr(lastIndex, string.length - lastIndex));
72-
return results.length === 0 ? [ string ] : results;
73-
}
101+
while(index < length) {
102+
if ( ((startIndex = text.indexOf(startSymbol, index)) != -1) &&
103+
((endIndex = text.indexOf(endSymbol, startIndex + startSymbolLength)) != -1) ) {
104+
(index != startIndex) && parts.push(text.substring(index, startIndex));
105+
parts.push(fn = $parse(exp = text.substring(startIndex + startSymbolLength, endIndex)));
106+
fn.exp = exp;
107+
index = endIndex + endSymbolLength;
108+
hasInterpolation = true;
109+
} else {
110+
// we did not find anything, so we have to add the remainder to the parts array
111+
(index != length) && parts.push(text.substring(index));
112+
index = length;
113+
}
114+
}
74115

75-
function binding(string) {
76-
var binding = string.replace(/\n/gm, ' ').match(/^\{\{(.*)\}\}$/);
77-
return binding ? binding[1] : null;
78-
}
116+
if (!(length = parts.length)) {
117+
// we added, nothing, must have been an empty string.
118+
parts.push('');
119+
length = 1;
120+
}
79121

80-
function hasBindings(bindings) {
81-
return bindings.length > 1 || binding(bindings[0]) !== null;
122+
if (!mustHaveExpression || hasInterpolation) {
123+
concat.length = length;
124+
fn = function(context) {
125+
for(var i = 0, ii = length, part; i<ii; i++) {
126+
if (typeof (part = parts[i]) == 'function') {
127+
part = part(context);
128+
if (part == null || part == undefined) {
129+
part = '';
130+
} else if (typeof part != 'string') {
131+
part = toJson(part);
132+
}
133+
}
134+
concat[i] = part;
135+
}
136+
return concat.join('');
137+
};
138+
fn.exp = text;
139+
fn.parts = parts;
140+
return fn;
141+
}
142+
};
143+
}];
82144
}
145+

test/markupSpec.js

Lines changed: 0 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -170,91 +170,5 @@ describe("markups", function() {
170170
expect(sortedHtml(element)).toEqual('<div href="some" ng:bind-attr="{"href":"some"}"></div>');
171171
dealoc(element);
172172
}));
173-
174-
it('should Parse Text With No Bindings', inject(function($rootScope, $compile) {
175-
var parts = parseBindings("a");
176-
expect(parts.length).toBe(1);
177-
expect(parts[0]).toBe("a");
178-
expect(binding(parts[0])).toBeFalsy();
179-
}));
180-
181-
it('should Parse Empty Text', inject(function($rootScope, $compile) {
182-
var parts = parseBindings("");
183-
expect(parts.length).toBe(1);
184-
expect(parts[0]).toBe("");
185-
expect(binding(parts[0])).toBeFalsy();
186-
}));
187-
188-
it('should Parse Inner Binding', inject(function($rootScope, $compile) {
189-
var parts = parseBindings("a{{b}}C");
190-
expect(parts.length).toBe(3);
191-
expect(parts[0]).toBe("a");
192-
expect(binding(parts[0])).toBeFalsy();
193-
expect(parts[1]).toBe("{{b}}");
194-
expect(binding(parts[1])).toBe("b");
195-
expect(parts[2]).toBe("C");
196-
expect(binding(parts[2])).toBeFalsy();
197-
}));
198-
199-
it('should Parse Ending Binding', inject(function($rootScope, $compile) {
200-
var parts = parseBindings("a{{b}}");
201-
expect(parts.length).toBe(2);
202-
expect(parts[0]).toBe("a");
203-
expect(binding(parts[0])).toBeFalsy();
204-
expect(parts[1]).toBe("{{b}}");
205-
expect(binding(parts[1])).toBe("b");
206-
}));
207-
208-
it('should Parse Begging Binding', inject(function($rootScope, $compile) {
209-
var parts = parseBindings("{{b}}c");
210-
expect(parts.length).toBe(2);
211-
expect(parts[0]).toBe("{{b}}");
212-
expect(binding(parts[0])).toBe("b");
213-
expect(parts[1]).toBe("c");
214-
expect(binding(parts[1])).toBeFalsy();
215-
}));
216-
217-
it('should Parse Loan Binding', inject(function($rootScope, $compile) {
218-
var parts = parseBindings("{{b}}");
219-
expect(parts.length).toBe(1);
220-
expect(parts[0]).toBe("{{b}}");
221-
expect(binding(parts[0])).toBe("b");
222-
}));
223-
224-
it('should Parse Two Bindings', inject(function($rootScope, $compile) {
225-
var parts = parseBindings("{{b}}{{c}}");
226-
expect(parts.length).toBe(2);
227-
expect(parts[0]).toBe("{{b}}");
228-
expect(binding(parts[0])).toBe("b");
229-
expect(parts[1]).toBe("{{c}}");
230-
expect(binding(parts[1])).toBe("c");
231-
}));
232-
233-
it('should Parse Two Bindings With Text In Middle', inject(function($rootScope, $compile) {
234-
var parts = parseBindings("{{b}}x{{c}}");
235-
expect(parts.length).toBe(3);
236-
expect(parts[0]).toBe("{{b}}");
237-
expect(binding(parts[0])).toBe("b");
238-
expect(parts[1]).toBe("x");
239-
expect(binding(parts[1])).toBeFalsy();
240-
expect(parts[2]).toBe("{{c}}");
241-
expect(binding(parts[2])).toBe("c");
242-
}));
243-
244-
it('should Parse Multiline', inject(function($rootScope, $compile) {
245-
var parts = parseBindings('"X\nY{{A\nB}}C\nD"');
246-
expect(binding('{{A\nB}}')).toBeTruthy();
247-
expect(parts.length).toBe(3);
248-
expect(parts[0]).toBe('"X\nY');
249-
expect(parts[1]).toBe('{{A\nB}}');
250-
expect(parts[2]).toBe('C\nD"');
251-
}));
252-
253-
it('should Has Binding', inject(function($rootScope, $compile) {
254-
expect(hasBindings(parseBindings("{{a}}"))).toBe(true);
255-
expect(hasBindings(parseBindings("a"))).toBeFalsy();
256-
expect(hasBindings(parseBindings("{{b}}x{{c}}"))).toBe(true);
257-
}));
258-
259173
});
260174

0 commit comments

Comments
 (0)