@@ -114,28 +114,24 @@ function $InterpolateProvider() {
114114 * result through {@link ng.$sce#getTrusted $sce.getTrusted(interpolatedResult,
115115 * trustedContext)} before returning it. Refer to the {@link ng.$sce $sce} service that
116116 * provides Strict Contextual Escaping for details.
117- * @returns {Object } An object describing the interpolation template string.
117+ * @returns {function(context) } an interpolation function which is used to compute the
118+ * interpolated string. The function has these parameters:
118119 *
119- * The properties of the returned object include:
120- *
121- * - `template` — `{string}` — original interpolation template string.
122- * - `separators` — `{Array.<string>}` — array of separators extracted from the template.
123- * - `expressions` — `{Array.<string>}` — array of expressions extracted from the template.
124- * - `compute` — {function(Array)()} — function that when called with an array of values will
125- * compute the result of interpolation for the given interpolation template and values.
120+ * - `context`: evaluation context for all expressions embedded in the interpolated text
126121 */
127122 function $interpolate ( text , mustHaveExpression , trustedContext ) {
128123 var startIndex ,
129124 endIndex ,
130125 index = 0 ,
131126 separators = [ ] ,
132127 expressions = [ ] ,
128+ parseFns = [ ] ,
133129 textLength = text . length ,
134130 hasInterpolation = false ,
135131 hasText = false ,
136- fn ,
137132 exp ,
138- concat = [ ] ;
133+ concat = [ ] ,
134+ lastValuesCache = { values : { } , results : { } } ;
139135
140136 while ( index < textLength ) {
141137 if ( ( ( startIndex = text . indexOf ( startSymbol , index ) ) != - 1 ) &&
@@ -144,6 +140,7 @@ function $InterpolateProvider() {
144140 separators . push ( text . substring ( index , startIndex ) ) ;
145141 exp = text . substring ( startIndex + startSymbolLength , endIndex ) ;
146142 expressions . push ( exp ) ;
143+ parseFns . push ( $parse ( exp ) ) ;
147144 index = endIndex + endSymbolLength ;
148145 hasInterpolation = true ;
149146 } else {
@@ -176,31 +173,16 @@ function $InterpolateProvider() {
176173 if ( ! mustHaveExpression || hasInterpolation ) {
177174 concat . length = separators . length + expressions . length ;
178175
179- return extend ( function interpolationFn ( scope ) {
180- var values = [ ] ;
181- forEach ( interpolationFn . expressions , function ( expression ) {
182- values . push ( scope . $eval ( expression ) ) ;
183- } ) ;
184- return interpolationFn . compute ( values ) ;
185- } , {
186- exp : text , //deprecated
187- template : text ,
188- separators : separators ,
189- expressions : expressions ,
190- compute : function ( values ) {
191- for ( var i = 0 , ii = expressions . length ; i < ii ; i ++ ) {
192- concat [ 2 * i ] = separators [ i ] ;
193- concat [ ( 2 * i ) + 1 ] = stringify ( values [ i ] ) ;
194- }
195- concat [ 2 * ii ] = separators [ ii ] ;
196- return concat . join ( '' ) ;
176+ var compute = function ( values ) {
177+ for ( var i = 0 , ii = expressions . length ; i < ii ; i ++ ) {
178+ concat [ 2 * i ] = separators [ i ] ;
179+ concat [ ( 2 * i ) + 1 ] = values [ i ] ;
197180 }
198- } ) ;
199- }
200-
201- function stringify ( value ) {
202- try {
181+ concat [ 2 * ii ] = separators [ ii ] ;
182+ return concat . join ( '' ) ;
183+ } ;
203184
185+ var stringify = function ( value ) {
204186 if ( trustedContext ) {
205187 value = $sce . getTrusted ( trustedContext , value ) ;
206188 } else {
@@ -214,12 +196,59 @@ function $InterpolateProvider() {
214196 }
215197
216198 return value ;
199+ } ;
217200
218- } catch ( err ) {
219- var newErr = $interpolateMinErr ( 'interr' , "Can't interpolate: {0}\n{1}" , text ,
220- err . toString ( ) ) ;
221- $exceptionHandler ( newErr ) ;
222- }
201+ return extend ( function interpolationFn ( context ) {
202+ var scopeId = context . $id || 'notAScope' ;
203+ var lastValues = lastValuesCache . values [ scopeId ] ;
204+ var lastResult = lastValuesCache . results [ scopeId ] ;
205+ var i = 0 ;
206+ var ii = expressions . length ;
207+ var values = new Array ( ii ) ;
208+ var val ;
209+ var inputsChanged = lastResult === undefined ? true : false ;
210+
211+
212+ // if we haven't seen this context before, initialize the cache and try to setup
213+ // a cleanup routine that purges the cache when the scope goes away.
214+ if ( ! lastValues ) {
215+ lastValues = [ ] ;
216+ inputsChanged = true ;
217+ if ( context . $on ) {
218+ context . $on ( '$destroy' , function ( ) {
219+ lastValuesCache . values [ scopeId ] = null ;
220+ lastValuesCache . results [ scopeId ] = null ;
221+ } ) ;
222+ }
223+ }
224+
225+
226+ try {
227+ for ( ; i < ii ; i ++ ) {
228+ val = stringify ( parseFns [ i ] ( context ) ) ;
229+ if ( val !== lastValues [ i ] ) {
230+ inputsChanged = true ;
231+ }
232+ values [ i ] = val ;
233+ }
234+
235+ if ( inputsChanged ) {
236+ lastValuesCache . values [ scopeId ] = values ;
237+ lastValuesCache . results [ scopeId ] = lastResult = compute ( values ) ;
238+ }
239+ } catch ( err ) {
240+ var newErr = $interpolateMinErr ( 'interr' , "Can't interpolate: {0}\n{1}" , text ,
241+ err . toString ( ) ) ;
242+ $exceptionHandler ( newErr ) ;
243+ }
244+
245+ return lastResult ;
246+ } , {
247+ // all of these properties are undocumented for now
248+ exp : text , //just for compatibility with regular watchers created via $watch
249+ separators : separators ,
250+ expressions : expressions
251+ } ) ;
223252 }
224253 }
225254
0 commit comments