Impure pipes need to live on the view that used them and need a new instance for each call site. Impure pipes need to live on the component view, cached across all child views, and need a new pure proxy for each for each call site that lives on the view of the call site. Fixes #8408 This bug was introduced not long ago by 152a117d5c27e56d1b32d69df2f69d34b94c0760
		
			
				
	
	
		
			202 lines
		
	
	
		
			7.8 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			202 lines
		
	
	
		
			7.8 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
| import {ViewType} from '../../core_private';
 | |
| 
 | |
| import {isPresent, isBlank} from '../../src/facade/lang';
 | |
| import {ListWrapper} from '../../src/facade/collection';
 | |
| 
 | |
| import * as o from '../output/output_ast';
 | |
| import {EventHandlerVars} from './constants';
 | |
| import {CompileQuery, createQueryList, addQueryToTokenMap} from './compile_query';
 | |
| import {NameResolver} from './expression_converter';
 | |
| import {CompileElement, CompileNode} from './compile_element';
 | |
| import {CompileMethod} from './compile_method';
 | |
| import {CompilePipe} from './compile_pipe';
 | |
| import {
 | |
|   CompileDirectiveMetadata,
 | |
|   CompilePipeMetadata,
 | |
|   CompileIdentifierMetadata,
 | |
|   CompileTokenMap
 | |
| } from '../compile_metadata';
 | |
| import {
 | |
|   getViewFactoryName,
 | |
|   injectFromViewParentInjector,
 | |
|   createDiTokenExpression,
 | |
|   getPropertyInView,
 | |
|   createPureProxy
 | |
| } from './util';
 | |
| import {CompilerConfig} from '../config';
 | |
| import {CompileBinding} from './compile_binding';
 | |
| import {Identifiers} from '../identifiers';
 | |
| 
 | |
| export class CompileView implements NameResolver {
 | |
|   public viewType: ViewType;
 | |
|   public viewQueries: CompileTokenMap<CompileQuery[]>;
 | |
| 
 | |
|   public nodes: CompileNode[] = [];
 | |
|   // root nodes or AppElements for ViewContainers
 | |
|   public rootNodesOrAppElements: o.Expression[] = [];
 | |
| 
 | |
|   public bindings: CompileBinding[] = [];
 | |
| 
 | |
|   public classStatements: o.Statement[] = [];
 | |
|   public createMethod: CompileMethod;
 | |
|   public injectorGetMethod: CompileMethod;
 | |
|   public updateContentQueriesMethod: CompileMethod;
 | |
|   public dirtyParentQueriesMethod: CompileMethod;
 | |
|   public updateViewQueriesMethod: CompileMethod;
 | |
|   public detectChangesInInputsMethod: CompileMethod;
 | |
|   public detectChangesRenderPropertiesMethod: CompileMethod;
 | |
|   public afterContentLifecycleCallbacksMethod: CompileMethod;
 | |
|   public afterViewLifecycleCallbacksMethod: CompileMethod;
 | |
|   public destroyMethod: CompileMethod;
 | |
|   public eventHandlerMethods: o.ClassMethod[] = [];
 | |
| 
 | |
|   public fields: o.ClassField[] = [];
 | |
|   public getters: o.ClassGetter[] = [];
 | |
|   public disposables: o.Expression[] = [];
 | |
|   public subscriptions: o.Expression[] = [];
 | |
| 
 | |
|   public componentView: CompileView;
 | |
|   public purePipes = new Map<string, CompilePipe>();
 | |
|   public pipes: CompilePipe[] = [];
 | |
|   public locals = new Map<string, o.Expression>();
 | |
|   public className: string;
 | |
|   public classType: o.Type;
 | |
|   public viewFactory: o.ReadVarExpr;
 | |
| 
 | |
|   public literalArrayCount = 0;
 | |
|   public literalMapCount = 0;
 | |
|   public pipeCount = 0;
 | |
| 
 | |
|   public componentContext: o.Expression;
 | |
| 
 | |
|   constructor(public component: CompileDirectiveMetadata, public genConfig: CompilerConfig,
 | |
|               public pipeMetas: CompilePipeMetadata[], public styles: o.Expression,
 | |
|               public viewIndex: number, public declarationElement: CompileElement,
 | |
|               public templateVariableBindings: string[][]) {
 | |
|     this.createMethod = new CompileMethod(this);
 | |
|     this.injectorGetMethod = new CompileMethod(this);
 | |
|     this.updateContentQueriesMethod = new CompileMethod(this);
 | |
|     this.dirtyParentQueriesMethod = new CompileMethod(this);
 | |
|     this.updateViewQueriesMethod = new CompileMethod(this);
 | |
|     this.detectChangesInInputsMethod = new CompileMethod(this);
 | |
|     this.detectChangesRenderPropertiesMethod = new CompileMethod(this);
 | |
| 
 | |
|     this.afterContentLifecycleCallbacksMethod = new CompileMethod(this);
 | |
|     this.afterViewLifecycleCallbacksMethod = new CompileMethod(this);
 | |
|     this.destroyMethod = new CompileMethod(this);
 | |
| 
 | |
|     this.viewType = getViewType(component, viewIndex);
 | |
|     this.className = `_View_${component.type.name}${viewIndex}`;
 | |
|     this.classType = o.importType(new CompileIdentifierMetadata({name: this.className}));
 | |
|     this.viewFactory = o.variable(getViewFactoryName(component, viewIndex));
 | |
|     if (this.viewType === ViewType.COMPONENT || this.viewType === ViewType.HOST) {
 | |
|       this.componentView = this;
 | |
|     } else {
 | |
|       this.componentView = this.declarationElement.view.componentView;
 | |
|     }
 | |
|     this.componentContext =
 | |
|         getPropertyInView(o.THIS_EXPR.prop('context'), this, this.componentView);
 | |
| 
 | |
|     var viewQueries = new CompileTokenMap<CompileQuery[]>();
 | |
|     if (this.viewType === ViewType.COMPONENT) {
 | |
|       var directiveInstance = o.THIS_EXPR.prop('context');
 | |
|       ListWrapper.forEachWithIndex(this.component.viewQueries, (queryMeta, queryIndex) => {
 | |
|         var propName = `_viewQuery_${queryMeta.selectors[0].name}_${queryIndex}`;
 | |
|         var queryList = createQueryList(queryMeta, directiveInstance, propName, this);
 | |
|         var query = new CompileQuery(queryMeta, queryList, directiveInstance, this);
 | |
|         addQueryToTokenMap(viewQueries, query);
 | |
|       });
 | |
|       var constructorViewQueryCount = 0;
 | |
|       this.component.type.diDeps.forEach((dep) => {
 | |
|         if (isPresent(dep.viewQuery)) {
 | |
|           var queryList = o.THIS_EXPR.prop('declarationAppElement')
 | |
|                               .prop('componentConstructorViewQueries')
 | |
|                               .key(o.literal(constructorViewQueryCount++));
 | |
|           var query = new CompileQuery(dep.viewQuery, queryList, null, this);
 | |
|           addQueryToTokenMap(viewQueries, query);
 | |
|         }
 | |
|       });
 | |
|     }
 | |
|     this.viewQueries = viewQueries;
 | |
|     templateVariableBindings.forEach(
 | |
|         (entry) => { this.locals.set(entry[1], o.THIS_EXPR.prop('context').prop(entry[0])); });
 | |
| 
 | |
|     if (!this.declarationElement.isNull()) {
 | |
|       this.declarationElement.setEmbeddedView(this);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   callPipe(name: string, input: o.Expression, args: o.Expression[]): o.Expression {
 | |
|     return CompilePipe.call(this, name, [input].concat(args));
 | |
|   }
 | |
| 
 | |
|   getLocal(name: string): o.Expression {
 | |
|     if (name == EventHandlerVars.event.name) {
 | |
|       return EventHandlerVars.event;
 | |
|     }
 | |
|     var currView: CompileView = this;
 | |
|     var result = currView.locals.get(name);
 | |
|     while (isBlank(result) && isPresent(currView.declarationElement.view)) {
 | |
|       currView = currView.declarationElement.view;
 | |
|       result = currView.locals.get(name);
 | |
|     }
 | |
|     if (isPresent(result)) {
 | |
|       return getPropertyInView(result, this, currView);
 | |
|     } else {
 | |
|       return null;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   createLiteralArray(values: o.Expression[]): o.Expression {
 | |
|     if (values.length === 0) {
 | |
|       return o.importExpr(Identifiers.EMPTY_ARRAY);
 | |
|     }
 | |
|     var proxyExpr = o.THIS_EXPR.prop(`_arr_${this.literalArrayCount++}`);
 | |
|     var proxyParams: o.FnParam[] = [];
 | |
|     var proxyReturnEntries: o.Expression[] = [];
 | |
|     for (var i = 0; i < values.length; i++) {
 | |
|       var paramName = `p${i}`;
 | |
|       proxyParams.push(new o.FnParam(paramName));
 | |
|       proxyReturnEntries.push(o.variable(paramName));
 | |
|     }
 | |
|     createPureProxy(o.fn(proxyParams, [new o.ReturnStatement(o.literalArr(proxyReturnEntries))]),
 | |
|                     values.length, proxyExpr, this);
 | |
|     return proxyExpr.callFn(values);
 | |
|   }
 | |
| 
 | |
|   createLiteralMap(entries: Array<Array<string | o.Expression>>): o.Expression {
 | |
|     if (entries.length === 0) {
 | |
|       return o.importExpr(Identifiers.EMPTY_MAP);
 | |
|     }
 | |
|     var proxyExpr = o.THIS_EXPR.prop(`_map_${this.literalMapCount++}`);
 | |
|     var proxyParams: o.FnParam[] = [];
 | |
|     var proxyReturnEntries: Array<Array<string | o.Expression>> = [];
 | |
|     var values: o.Expression[] = [];
 | |
|     for (var i = 0; i < entries.length; i++) {
 | |
|       var paramName = `p${i}`;
 | |
|       proxyParams.push(new o.FnParam(paramName));
 | |
|       proxyReturnEntries.push([entries[i][0], o.variable(paramName)]);
 | |
|       values.push(<o.Expression>entries[i][1]);
 | |
|     }
 | |
|     createPureProxy(o.fn(proxyParams, [new o.ReturnStatement(o.literalMap(proxyReturnEntries))]),
 | |
|                     entries.length, proxyExpr, this);
 | |
|     return proxyExpr.callFn(values);
 | |
|   }
 | |
| 
 | |
|   afterNodes() {
 | |
|     this.pipes.forEach((pipe) => pipe.create());
 | |
|     this.viewQueries.values().forEach(
 | |
|         (queries) => queries.forEach((query) => query.afterChildren(this.updateViewQueriesMethod)));
 | |
|   }
 | |
| }
 | |
| 
 | |
| function getViewType(component: CompileDirectiveMetadata, embeddedTemplateIndex: number): ViewType {
 | |
|   if (embeddedTemplateIndex > 0) {
 | |
|     return ViewType.EMBEDDED;
 | |
|   } else if (component.type.isHost) {
 | |
|     return ViewType.HOST;
 | |
|   } else {
 | |
|     return ViewType.COMPONENT;
 | |
|   }
 | |
| }
 |