@@ -127,12 +127,11 @@ function $InterpolateProvider() {
127
127
var startIndex ,
128
128
endIndex ,
129
129
index = 0 ,
130
- parts = [ ] ,
131
130
length = text . length ,
132
131
hasInterpolation = false ,
133
- fn ,
132
+ fn = null ,
134
133
exp ,
135
- concat = [ ] ;
134
+ parts = [ ] ;
136
135
137
136
while ( index < length ) {
138
137
if ( ( ( startIndex = text . indexOf ( startSymbol , index ) ) != - 1 ) &&
@@ -149,10 +148,9 @@ function $InterpolateProvider() {
149
148
}
150
149
}
151
150
152
- if ( ! ( length = parts . length ) ) {
151
+ if ( ! parts . length ) {
153
152
// we added, nothing, must have been an empty string.
154
153
parts . push ( '' ) ;
155
- length = 1 ;
156
154
}
157
155
158
156
// Concatenating expressions makes it hard to reason about whether some combination of
@@ -169,25 +167,29 @@ function $InterpolateProvider() {
169
167
}
170
168
171
169
if ( ! mustHaveExpression || hasInterpolation ) {
172
- concat . length = length ;
173
- fn = function ( context ) {
170
+ var concat = new Array ( parts . length ) ,
171
+ expressions = { } ;
172
+ forEach ( parts , function ( value , index ) {
173
+ if ( isFunction ( value ) ) {
174
+ expressions [ index ] = value ;
175
+ concat [ index ] = '' ;
176
+ } else {
177
+ concat [ index ] = value ;
178
+ }
179
+ } ) ;
180
+ // computes all the interpolations and returns the resulting string
181
+ // a specific index might already be computed (cz of the scope's dirty-checking),
182
+ // and so its expression shouldn't be executed a 2nd time
183
+ // also populates the lastValues of custom watchers for internal dirty-checking
184
+ var getTextValue = function ( scope , computedIndex , computedValue , lastValues ) {
174
185
try {
175
- for ( var i = 0 , ii = length , part ; i < ii ; i ++ ) {
176
- if ( typeof ( part = parts [ i ] ) == 'function' ) {
177
- part = part ( context ) ;
178
- if ( trustedContext ) {
179
- part = $sce . getTrusted ( trustedContext , part ) ;
180
- } else {
181
- part = $sce . valueOf ( part ) ;
182
- }
183
- if ( part === null || isUndefined ( part ) ) {
184
- part = '' ;
185
- } else if ( typeof part != 'string' ) {
186
- part = toJson ( part ) ;
187
- }
188
- }
189
- concat [ i ] = part ;
190
- }
186
+ forEach ( expressions , function ( expression , index ) {
187
+ concat [ index ] = index == computedIndex
188
+ ? computedValue
189
+ : getStringValue ( expression ( scope ) ) ;
190
+
191
+ if ( lastValues ) lastValues [ index ] = concat [ index ] ;
192
+ } ) ;
191
193
return concat . join ( '' ) ;
192
194
}
193
195
catch ( err ) {
@@ -196,10 +198,63 @@ function $InterpolateProvider() {
196
198
$exceptionHandler ( newErr ) ;
197
199
}
198
200
} ;
201
+ var getStringValue = function ( value ) {
202
+ value = trustedContext
203
+ ? $sce . getTrusted ( trustedContext , value )
204
+ : $sce . valueOf ( value ) ;
205
+
206
+ if ( value === null || isUndefined ( value ) ) {
207
+ return '' ;
208
+ }
209
+ return isString ( value ) ? value : toJson ( value ) ;
210
+ } ;
211
+
212
+ fn = function ( scope ) {
213
+ return getTextValue ( scope ) ;
214
+ } ;
199
215
fn . exp = text ;
200
216
fn . parts = parts ;
201
- return fn ;
217
+
218
+ // watches each interpolation separately for performance
219
+ fn . $$beWatched = function ( scope , origListener , objectEquality ) {
220
+ var lastTextValue , lastValues = { } , watchersRm = [ ] ;
221
+
222
+ forEach ( expressions , function ( expression , index ) {
223
+ watchersRm . push ( scope . $watch ( function watchInterpolatedExpr ( scope ) {
224
+ try {
225
+ return getStringValue ( expression ( scope ) ) ;
226
+ } catch ( err ) {
227
+ var newErr = $interpolateMinErr ( 'interr' , "Can't interpolate: {0}\n{1}" ,
228
+ text , err . toString ( ) ) ;
229
+ $exceptionHandler ( newErr ) ;
230
+ }
231
+ } , listenerOf ( index ) , objectEquality ) ) ;
232
+ } ) ;
233
+
234
+ function listenerOf ( index ) {
235
+ return function interpolatedExprListener ( value , oldValue ) {
236
+ // we only invoke the origListener if the current value
237
+ // is not equal to the last computed value
238
+ // ex: if in `{{a}}-{{b}}` both values change in a digest,
239
+ // the listener of `a` gets invoked first, we compute the string
240
+ // and invoke the origListener once,
241
+ // and ignore it when the listener of `b` gets triggered
242
+ // (unless the value of `b` changes again since the last computation)
243
+ if ( value !== lastValues [ index ] ) {
244
+ var textValue = getTextValue ( scope , index , value , lastValues ) ;
245
+ origListener . call ( this , textValue ,
246
+ value === oldValue ? textValue : lastTextValue , scope ) ;
247
+ lastTextValue = textValue ;
248
+ }
249
+ } ;
250
+ }
251
+
252
+ return function compositeWatchersRm ( ) {
253
+ forEach ( watchersRm , function ( wRm ) { wRm ( ) ; } ) ;
254
+ } ;
255
+ } ;
202
256
}
257
+ return fn ;
203
258
}
204
259
205
260
@@ -239,4 +294,3 @@ function $InterpolateProvider() {
239
294
return $interpolate ;
240
295
} ] ;
241
296
}
242
-
0 commit comments