mirror of
https://github.com/Hopiu/angular.js.git
synced 2026-03-16 23:30:23 +00:00
refactor($interpolate): improve interpolation service add documentation
This commit is contained in:
parent
0f6b2ef982
commit
5001c1a121
3 changed files with 230 additions and 161 deletions
|
|
@ -1,82 +1,145 @@
|
|||
'use strict';
|
||||
|
||||
function $InterpolateProvider(){
|
||||
this.$get = ['$parse', function($parse){
|
||||
return function(text, templateOnly) {
|
||||
var bindings = parseBindings(text);
|
||||
if (hasBindings(bindings) || !templateOnly) {
|
||||
return compileBindTemplate(text);
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name angular.module.ng.$interpolateProvider
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
*
|
||||
* Used for configuring the interpolation markup. Deafults to `{{` and `}}`.
|
||||
*/
|
||||
function $InterpolateProvider() {
|
||||
var startSymbol = '{{';
|
||||
var endSymbol = '}}';
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name angular.module.ng.$interpolateProvider#startSymbol
|
||||
* @methodOf angular.module.ng.$interpolateProvider
|
||||
* @description
|
||||
* Symbol to denote start of expression in the interpolated string. Defaults to `{{`.
|
||||
*
|
||||
* @prop {string=} value new value to set the starting symbol to.
|
||||
*/
|
||||
this.startSymbol = function(value){
|
||||
if (value) {
|
||||
startSymbol = value;
|
||||
return this;
|
||||
} else {
|
||||
return startSymbol;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name angular.module.ng.$interpolateProvider#endSymbol
|
||||
* @methodOf angular.module.ng.$interpolateProvider
|
||||
* @description
|
||||
* Symbol to denote the end of expression in the interpolated string. Defaults to `}}`.
|
||||
*
|
||||
* @prop {string=} value new value to set the ending symbol to.
|
||||
*/
|
||||
this.endSymbol = function(value){
|
||||
if (value) {
|
||||
endSymbol = value;
|
||||
return this;
|
||||
} else {
|
||||
return startSymbol;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
this.$get = ['$parse', function($parse) {
|
||||
var startSymbolLength = startSymbol.length,
|
||||
endSymbolLength = endSymbol.length;
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name angular.module.ng.$interpolate
|
||||
* @function
|
||||
*
|
||||
* @requires $parse
|
||||
*
|
||||
* @description
|
||||
*
|
||||
* Compiles a string with markup into an interpolation function. This service is used by the
|
||||
* HTML {@link angular.module.ng.$compile $compile} service for data binding. See
|
||||
* {@link angular.module.ng.$interpolateProvider $interpolateProvider} for configuring the
|
||||
* interpolation markup.
|
||||
*
|
||||
*
|
||||
<pre>
|
||||
var $interpolate = ...; // injected
|
||||
var exp = $interpolate('Hello {{name}}!');
|
||||
expect(exp({name:'Angular'}).toEqual('Hello Angular!');
|
||||
</pre>
|
||||
*
|
||||
*
|
||||
* @param {string} text The text with markup to interpolate.
|
||||
* @param {boolean=} mustHaveExpression if set to true then the interpolation string must have
|
||||
* embedded expression in order to return an interpolation function. Strings with no
|
||||
* embedded expression will return null for the interpolation function.
|
||||
* @returns {function(context)} an interpolation function which is used to compute the interpolated
|
||||
* string. The function has these parameters:
|
||||
*
|
||||
* * `context`: an object against which any expressions embedded in the strings are evaluated
|
||||
* against.
|
||||
*
|
||||
*/
|
||||
return function(text, mustHaveExpression) {
|
||||
var startIndex,
|
||||
endIndex,
|
||||
index = 0,
|
||||
parts = [],
|
||||
length = text.length,
|
||||
hasInterpolation = false,
|
||||
fn,
|
||||
exp,
|
||||
concat = [];
|
||||
|
||||
while(index < length) {
|
||||
if ( ((startIndex = text.indexOf(startSymbol, index)) != -1) &&
|
||||
((endIndex = text.indexOf(endSymbol, startIndex + startSymbolLength)) != -1) ) {
|
||||
(index != startIndex) && parts.push(text.substring(index, startIndex));
|
||||
parts.push(fn = $parse(exp = text.substring(startIndex + startSymbolLength, endIndex)));
|
||||
fn.exp = exp;
|
||||
index = endIndex + endSymbolLength;
|
||||
hasInterpolation = true;
|
||||
} else {
|
||||
// we did not find anything, so we have to add the remainder to the parts array
|
||||
(index != length) && parts.push(text.substring(index));
|
||||
index = length;
|
||||
}
|
||||
}
|
||||
|
||||
if (!(length = parts.length)) {
|
||||
// we added, nothing, must have been an empty string.
|
||||
parts.push('');
|
||||
length = 1;
|
||||
}
|
||||
|
||||
if (!mustHaveExpression || hasInterpolation) {
|
||||
concat.length = length;
|
||||
fn = function(context) {
|
||||
for(var i = 0, ii = length, part; i<ii; i++) {
|
||||
if (typeof (part = parts[i]) == 'function') {
|
||||
part = part(context);
|
||||
if (part == null || part == undefined) {
|
||||
part = '';
|
||||
} else if (typeof part != 'string') {
|
||||
part = toJson(part);
|
||||
}
|
||||
}
|
||||
concat[i] = part;
|
||||
}
|
||||
return concat.join('');
|
||||
};
|
||||
fn.exp = text;
|
||||
fn.parts = parts;
|
||||
return fn;
|
||||
}
|
||||
};
|
||||
}];
|
||||
}
|
||||
|
||||
var bindTemplateCache = {};
|
||||
function compileBindTemplate(template){
|
||||
var fn = bindTemplateCache[template];
|
||||
if (!fn) {
|
||||
var bindings = [];
|
||||
forEach(parseBindings(template), function(text){
|
||||
var exp = binding(text);
|
||||
bindings.push(exp
|
||||
? function(scope, element) { return scope.$eval(exp); }
|
||||
: function() { return text; });
|
||||
});
|
||||
bindTemplateCache[template] = fn = function(scope, element, prettyPrintJson) {
|
||||
var parts = [],
|
||||
hadOwnElement = scope.hasOwnProperty('$element'),
|
||||
oldElement = scope.$element;
|
||||
|
||||
// TODO(misko): get rid of $element
|
||||
scope.$element = element;
|
||||
try {
|
||||
for (var i = 0; i < bindings.length; i++) {
|
||||
var value = bindings[i](scope, element);
|
||||
if (isElement(value))
|
||||
value = '';
|
||||
else if (isObject(value))
|
||||
value = toJson(value, prettyPrintJson);
|
||||
parts.push(value);
|
||||
}
|
||||
return parts.join('');
|
||||
} finally {
|
||||
if (hadOwnElement) {
|
||||
scope.$element = oldElement;
|
||||
} else {
|
||||
delete scope.$element;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
return fn;
|
||||
}
|
||||
|
||||
|
||||
function parseBindings(string) {
|
||||
var results = [];
|
||||
var lastIndex = 0;
|
||||
var index;
|
||||
while((index = string.indexOf('{{', lastIndex)) > -1) {
|
||||
if (lastIndex < index)
|
||||
results.push(string.substr(lastIndex, index - lastIndex));
|
||||
lastIndex = index;
|
||||
|
||||
index = string.indexOf('}}', index);
|
||||
index = index < 0 ? string.length : index + 2;
|
||||
|
||||
results.push(string.substr(lastIndex, index - lastIndex));
|
||||
lastIndex = index;
|
||||
}
|
||||
if (lastIndex != string.length)
|
||||
results.push(string.substr(lastIndex, string.length - lastIndex));
|
||||
return results.length === 0 ? [ string ] : results;
|
||||
}
|
||||
|
||||
function binding(string) {
|
||||
var binding = string.replace(/\n/gm, ' ').match(/^\{\{(.*)\}\}$/);
|
||||
return binding ? binding[1] : null;
|
||||
}
|
||||
|
||||
function hasBindings(bindings) {
|
||||
return bindings.length > 1 || binding(bindings[0]) !== null;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -170,91 +170,5 @@ describe("markups", function() {
|
|||
expect(sortedHtml(element)).toEqual('<div href="some" ng:bind-attr="{"href":"some"}"></div>');
|
||||
dealoc(element);
|
||||
}));
|
||||
|
||||
it('should Parse Text With No Bindings', inject(function($rootScope, $compile) {
|
||||
var parts = parseBindings("a");
|
||||
expect(parts.length).toBe(1);
|
||||
expect(parts[0]).toBe("a");
|
||||
expect(binding(parts[0])).toBeFalsy();
|
||||
}));
|
||||
|
||||
it('should Parse Empty Text', inject(function($rootScope, $compile) {
|
||||
var parts = parseBindings("");
|
||||
expect(parts.length).toBe(1);
|
||||
expect(parts[0]).toBe("");
|
||||
expect(binding(parts[0])).toBeFalsy();
|
||||
}));
|
||||
|
||||
it('should Parse Inner Binding', inject(function($rootScope, $compile) {
|
||||
var parts = parseBindings("a{{b}}C");
|
||||
expect(parts.length).toBe(3);
|
||||
expect(parts[0]).toBe("a");
|
||||
expect(binding(parts[0])).toBeFalsy();
|
||||
expect(parts[1]).toBe("{{b}}");
|
||||
expect(binding(parts[1])).toBe("b");
|
||||
expect(parts[2]).toBe("C");
|
||||
expect(binding(parts[2])).toBeFalsy();
|
||||
}));
|
||||
|
||||
it('should Parse Ending Binding', inject(function($rootScope, $compile) {
|
||||
var parts = parseBindings("a{{b}}");
|
||||
expect(parts.length).toBe(2);
|
||||
expect(parts[0]).toBe("a");
|
||||
expect(binding(parts[0])).toBeFalsy();
|
||||
expect(parts[1]).toBe("{{b}}");
|
||||
expect(binding(parts[1])).toBe("b");
|
||||
}));
|
||||
|
||||
it('should Parse Begging Binding', inject(function($rootScope, $compile) {
|
||||
var parts = parseBindings("{{b}}c");
|
||||
expect(parts.length).toBe(2);
|
||||
expect(parts[0]).toBe("{{b}}");
|
||||
expect(binding(parts[0])).toBe("b");
|
||||
expect(parts[1]).toBe("c");
|
||||
expect(binding(parts[1])).toBeFalsy();
|
||||
}));
|
||||
|
||||
it('should Parse Loan Binding', inject(function($rootScope, $compile) {
|
||||
var parts = parseBindings("{{b}}");
|
||||
expect(parts.length).toBe(1);
|
||||
expect(parts[0]).toBe("{{b}}");
|
||||
expect(binding(parts[0])).toBe("b");
|
||||
}));
|
||||
|
||||
it('should Parse Two Bindings', inject(function($rootScope, $compile) {
|
||||
var parts = parseBindings("{{b}}{{c}}");
|
||||
expect(parts.length).toBe(2);
|
||||
expect(parts[0]).toBe("{{b}}");
|
||||
expect(binding(parts[0])).toBe("b");
|
||||
expect(parts[1]).toBe("{{c}}");
|
||||
expect(binding(parts[1])).toBe("c");
|
||||
}));
|
||||
|
||||
it('should Parse Two Bindings With Text In Middle', inject(function($rootScope, $compile) {
|
||||
var parts = parseBindings("{{b}}x{{c}}");
|
||||
expect(parts.length).toBe(3);
|
||||
expect(parts[0]).toBe("{{b}}");
|
||||
expect(binding(parts[0])).toBe("b");
|
||||
expect(parts[1]).toBe("x");
|
||||
expect(binding(parts[1])).toBeFalsy();
|
||||
expect(parts[2]).toBe("{{c}}");
|
||||
expect(binding(parts[2])).toBe("c");
|
||||
}));
|
||||
|
||||
it('should Parse Multiline', inject(function($rootScope, $compile) {
|
||||
var parts = parseBindings('"X\nY{{A\nB}}C\nD"');
|
||||
expect(binding('{{A\nB}}')).toBeTruthy();
|
||||
expect(parts.length).toBe(3);
|
||||
expect(parts[0]).toBe('"X\nY');
|
||||
expect(parts[1]).toBe('{{A\nB}}');
|
||||
expect(parts[2]).toBe('C\nD"');
|
||||
}));
|
||||
|
||||
it('should Has Binding', inject(function($rootScope, $compile) {
|
||||
expect(hasBindings(parseBindings("{{a}}"))).toBe(true);
|
||||
expect(hasBindings(parseBindings("a"))).toBeFalsy();
|
||||
expect(hasBindings(parseBindings("{{b}}x{{c}}"))).toBe(true);
|
||||
}));
|
||||
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -13,9 +13,101 @@ describe('$interpolate', function() {
|
|||
expect($interpolate('some text', true)).toBeUndefined();
|
||||
}));
|
||||
|
||||
it('should suppress falsy objects', inject(function($interpolate) {
|
||||
expect($interpolate('{{undefined}}')()).toEqual('');
|
||||
expect($interpolate('{{null}}')()).toEqual('');
|
||||
expect($interpolate('{{a.b}}')()).toEqual('');
|
||||
}));
|
||||
|
||||
it('should jsonify objects', inject(function($interpolate) {
|
||||
expect($interpolate('{{ {} }}')()).toEqual('{}');
|
||||
expect($interpolate('{{ true }}')()).toEqual('true');
|
||||
expect($interpolate('{{ false }}')()).toEqual('false');
|
||||
}));
|
||||
|
||||
|
||||
it('should return interpolation function', inject(function($interpolate, $rootScope) {
|
||||
$rootScope.name = 'Misko';
|
||||
expect($interpolate('Hello {{name}}!')($rootScope)).toEqual('Hello Misko!');
|
||||
}));
|
||||
|
||||
describe('provider', function() {
|
||||
beforeEach(module(function($interpolateProvider) {
|
||||
$interpolateProvider.startSymbol('--');
|
||||
$interpolateProvider.endSymbol('--');
|
||||
}));
|
||||
|
||||
it('should not get confused with same markers', inject(function($interpolate) {
|
||||
expect($interpolate('---').parts).toEqual(['---']);
|
||||
expect($interpolate('----')()).toEqual('');
|
||||
expect($interpolate('--1--')()).toEqual('1');
|
||||
}));
|
||||
});
|
||||
|
||||
describe('parseBindings', function() {
|
||||
it('should Parse Text With No Bindings', inject(function($interpolate) {
|
||||
var parts = $interpolate("a").parts;
|
||||
expect(parts.length).toEqual(1);
|
||||
expect(parts[0]).toEqual("a");
|
||||
}));
|
||||
|
||||
it('should Parse Empty Text', inject(function($interpolate) {
|
||||
var parts = $interpolate("").parts;
|
||||
expect(parts.length).toEqual(1);
|
||||
expect(parts[0]).toEqual("");
|
||||
}));
|
||||
|
||||
it('should Parse Inner Binding', inject(function($interpolate) {
|
||||
var parts = $interpolate("a{{b}}C").parts;
|
||||
expect(parts.length).toEqual(3);
|
||||
expect(parts[0]).toEqual("a");
|
||||
expect(parts[1].exp).toEqual("b");
|
||||
expect(parts[1]({b:123})).toEqual(123);
|
||||
expect(parts[2]).toEqual("C");
|
||||
}));
|
||||
|
||||
it('should Parse Ending Binding', inject(function($interpolate) {
|
||||
var parts = $interpolate("a{{b}}").parts;
|
||||
expect(parts.length).toEqual(2);
|
||||
expect(parts[0]).toEqual("a");
|
||||
expect(parts[1].exp).toEqual("b");
|
||||
expect(parts[1]({b:123})).toEqual(123);
|
||||
}));
|
||||
|
||||
it('should Parse Begging Binding', inject(function($interpolate) {
|
||||
var parts = $interpolate("{{b}}c").parts;
|
||||
expect(parts.length).toEqual(2);
|
||||
expect(parts[0].exp).toEqual("b");
|
||||
expect(parts[1]).toEqual("c");
|
||||
}));
|
||||
|
||||
it('should Parse Loan Binding', inject(function($interpolate) {
|
||||
var parts = $interpolate("{{b}}").parts;
|
||||
expect(parts.length).toEqual(1);
|
||||
expect(parts[0].exp).toEqual("b");
|
||||
}));
|
||||
|
||||
it('should Parse Two Bindings', inject(function($interpolate) {
|
||||
var parts = $interpolate("{{b}}{{c}}").parts;
|
||||
expect(parts.length).toEqual(2);
|
||||
expect(parts[0].exp).toEqual("b");
|
||||
expect(parts[1].exp).toEqual("c");
|
||||
}));
|
||||
|
||||
it('should Parse Two Bindings With Text In Middle', inject(function($interpolate) {
|
||||
var parts = $interpolate("{{b}}x{{c}}").parts;
|
||||
expect(parts.length).toEqual(3);
|
||||
expect(parts[0].exp).toEqual("b");
|
||||
expect(parts[1]).toEqual("x");
|
||||
expect(parts[2].exp).toEqual("c");
|
||||
}));
|
||||
|
||||
it('should Parse Multiline', inject(function($interpolate) {
|
||||
var parts = $interpolate('"X\nY{{A\n+B}}C\nD"').parts;
|
||||
expect(parts.length).toEqual(3);
|
||||
expect(parts[0]).toEqual('"X\nY');
|
||||
expect(parts[1].exp).toEqual('A\n+B');
|
||||
expect(parts[2]).toEqual('C\nD"');
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue