feat(ivy): support animation @triggers in templates (#25849)
PR Close #25849
This commit is contained in:
		
							parent
							
								
									ed266daf2c
								
							
						
					
					
						commit
						e3633888ed
					
				| @ -836,7 +836,7 @@ describe('compiler compliance', () => { | |||||||
|         }; |         }; | ||||||
| 
 | 
 | ||||||
|         const MyAppDefinition = ` |         const MyAppDefinition = ` | ||||||
|           const $e0_attr$ = [${AttributeMarker.SelectOnly}, "names"];  |           const $e0_attr$ = [${AttributeMarker.SelectOnly}, "names"]; | ||||||
|           const $e0_ff$ = function ($v0$, $v1$, $v2$, $v3$, $v4$, $v5$, $v6$, $v7$, $v8$) { |           const $e0_ff$ = function ($v0$, $v1$, $v2$, $v3$, $v4$, $v5$, $v6$, $v7$, $v8$) { | ||||||
|             return ["start-", $v0$, $v1$, $v2$, $v3$, $v4$, "-middle-", $v5$, $v6$, $v7$, $v8$, "-end"]; |             return ["start-", $v0$, $v1$, $v2$, $v3$, $v4$, "-middle-", $v5$, $v6$, $v7$, $v8$, "-end"]; | ||||||
|           } |           } | ||||||
|  | |||||||
| @ -180,6 +180,52 @@ describe('compiler compliance: styling', () => { | |||||||
|       const result = compile(files, angularFiles); |       const result = compile(files, angularFiles); | ||||||
|       expectEmit(result.source, template, 'Incorrect template'); |       expectEmit(result.source, template, 'Incorrect template'); | ||||||
|     }); |     }); | ||||||
|  | 
 | ||||||
|  |     it('should generate any animation triggers into the component template', () => { | ||||||
|  |       const files = { | ||||||
|  |         app: { | ||||||
|  |           'spec.ts': ` | ||||||
|  |                 import {Component, NgModule} from '@angular/core'; | ||||||
|  | 
 | ||||||
|  |                 @Component({ | ||||||
|  |                   selector: "my-component", | ||||||
|  |                   template: \` | ||||||
|  |                     <div [@foo]='exp'></div> | ||||||
|  |                     <div @bar></div> | ||||||
|  |                     <div [@baz]></div>\`,
 | ||||||
|  |                 }) | ||||||
|  |                 export class MyComponent { | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 @NgModule({declarations: [MyComponent]}) | ||||||
|  |                 export class MyModule {} | ||||||
|  |             ` | ||||||
|  |         } | ||||||
|  |       }; | ||||||
|  | 
 | ||||||
|  |       const template = ` | ||||||
|  |         const $e0_attrs$ = ["@foo", ""]; | ||||||
|  |         const $e1_attrs$ = ["@bar", ""]; | ||||||
|  |         const $e2_attrs$ = ["@baz", ""]; | ||||||
|  |         … | ||||||
|  |         MyComponent.ngComponentDef = $r3$.ɵdefineComponent({ | ||||||
|  |           … | ||||||
|  |           template:  function MyComponent_Template(rf, $ctx$) { | ||||||
|  |             if (rf & 1) { | ||||||
|  |               $r3$.ɵelement(0, "div", $e0_attrs$); | ||||||
|  |               $r3$.ɵelement(1, "div", $e1_attrs$); | ||||||
|  |               $r3$.ɵelement(2, "div", $e2_attrs$); | ||||||
|  |             } | ||||||
|  |             if (rf & 2) { | ||||||
|  |               $r3$.ɵelementAttribute(0, "@foo", $r3$.ɵbind(ctx.exp)); | ||||||
|  |             } | ||||||
|  |           } | ||||||
|  |         }); | ||||||
|  |       `;
 | ||||||
|  | 
 | ||||||
|  |       const result = compile(files, angularFiles); | ||||||
|  |       expectEmit(result.source, template, 'Incorrect template'); | ||||||
|  |     }); | ||||||
|   }); |   }); | ||||||
| 
 | 
 | ||||||
|   describe('[style] and [style.prop]', () => { |   describe('[style] and [style.prop]', () => { | ||||||
|  | |||||||
| @ -36,10 +36,11 @@ function mapBindingToInstruction(type: BindingType): o.ExternalReference|undefin | |||||||
|   switch (type) { |   switch (type) { | ||||||
|     case BindingType.Property: |     case BindingType.Property: | ||||||
|       return R3.elementProperty; |       return R3.elementProperty; | ||||||
|     case BindingType.Attribute: |  | ||||||
|       return R3.elementAttribute; |  | ||||||
|     case BindingType.Class: |     case BindingType.Class: | ||||||
|       return R3.elementClassProp; |       return R3.elementClassProp; | ||||||
|  |     case BindingType.Attribute: | ||||||
|  |     case BindingType.Animation: | ||||||
|  |       return R3.elementAttribute; | ||||||
|     default: |     default: | ||||||
|       return undefined; |       return undefined; | ||||||
|   } |   } | ||||||
| @ -459,7 +460,7 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver | |||||||
|         initialClassDeclarations.length || classInputs.length; |         initialClassDeclarations.length || classInputs.length; | ||||||
| 
 | 
 | ||||||
|     // add attributes for directive matching purposes
 |     // add attributes for directive matching purposes
 | ||||||
|     attributes.push(...this.prepareSelectOnlyAttrs(allOtherInputs, element.outputs)); |     attributes.push(...this.prepareSyntheticAndSelectOnlyAttrs(allOtherInputs, element.outputs)); | ||||||
|     parameters.push(this.toAttrsParam(attributes)); |     parameters.push(this.toAttrsParam(attributes)); | ||||||
| 
 | 
 | ||||||
|     // local refs (ex.: <div #foo #bar="baz">)
 |     // local refs (ex.: <div #foo #bar="baz">)
 | ||||||
| @ -608,20 +609,27 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver | |||||||
| 
 | 
 | ||||||
|     // Generate element input bindings
 |     // Generate element input bindings
 | ||||||
|     allOtherInputs.forEach((input: t.BoundAttribute) => { |     allOtherInputs.forEach((input: t.BoundAttribute) => { | ||||||
|       if (input.type === BindingType.Animation) { |  | ||||||
|         console.error('warning: animation bindings not yet supported'); |  | ||||||
|         return; |  | ||||||
|       } |  | ||||||
| 
 |  | ||||||
|       const instruction = mapBindingToInstruction(input.type); |       const instruction = mapBindingToInstruction(input.type); | ||||||
|       if (instruction) { |       if (input.type === BindingType.Animation) { | ||||||
|  |         const value = input.value.visit(this._valueConverter); | ||||||
|  |         // setAttribute without a value doesn't make any sense
 | ||||||
|  |         if (value.name || value.value) { | ||||||
|  |           const name = prepareSyntheticAttributeName(input.name); | ||||||
|  |           this.updateInstruction(input.sourceSpan, R3.elementAttribute, () => { | ||||||
|  |             return [ | ||||||
|  |               o.literal(elementIndex), o.literal(name), this.convertPropertyBinding(implicit, value) | ||||||
|  |             ]; | ||||||
|  |           }); | ||||||
|  |         } | ||||||
|  |       } else if (instruction) { | ||||||
|         const params: any[] = []; |         const params: any[] = []; | ||||||
|         const sanitizationRef = resolveSanitizationFn(input, input.securityContext); |         const sanitizationRef = resolveSanitizationFn(input, input.securityContext); | ||||||
|         if (sanitizationRef) params.push(sanitizationRef); |         if (sanitizationRef) params.push(sanitizationRef); | ||||||
| 
 | 
 | ||||||
|         // TODO(chuckj): runtime: security context?
 |         // TODO(chuckj): runtime: security context
 | ||||||
|         const value = input.value.visit(this._valueConverter); |         const value = input.value.visit(this._valueConverter); | ||||||
|         this.allocateBindingSlots(value); |         this.allocateBindingSlots(value); | ||||||
|  | 
 | ||||||
|         this.updateInstruction(input.sourceSpan, instruction, () => { |         this.updateInstruction(input.sourceSpan, instruction, () => { | ||||||
|           return [ |           return [ | ||||||
|             o.literal(elementIndex), o.literal(input.name), |             o.literal(elementIndex), o.literal(input.name), | ||||||
| @ -680,7 +688,7 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver | |||||||
|     const attrsExprs: o.Expression[] = []; |     const attrsExprs: o.Expression[] = []; | ||||||
|     template.attributes.forEach( |     template.attributes.forEach( | ||||||
|         (a: t.TextAttribute) => { attrsExprs.push(asLiteral(a.name), asLiteral(a.value)); }); |         (a: t.TextAttribute) => { attrsExprs.push(asLiteral(a.name), asLiteral(a.value)); }); | ||||||
|     attrsExprs.push(...this.prepareSelectOnlyAttrs(template.inputs, template.outputs)); |     attrsExprs.push(...this.prepareSyntheticAndSelectOnlyAttrs(template.inputs, template.outputs)); | ||||||
|     parameters.push(this.toAttrsParam(attrsExprs)); |     parameters.push(this.toAttrsParam(attrsExprs)); | ||||||
| 
 | 
 | ||||||
|     // local refs (ex.: <ng-template #foo>)
 |     // local refs (ex.: <ng-template #foo>)
 | ||||||
| @ -856,14 +864,30 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver | |||||||
|     return attributesMap; |     return attributesMap; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   private prepareSelectOnlyAttrs(inputs: t.BoundAttribute[], outputs: t.BoundEvent[]): |   private prepareSyntheticAndSelectOnlyAttrs(inputs: t.BoundAttribute[], outputs: t.BoundEvent[]): | ||||||
|       o.Expression[] { |       o.Expression[] { | ||||||
|     const attrExprs: o.Expression[] = []; |     const attrExprs: o.Expression[] = []; | ||||||
|  |     const nonSyntheticInputs: t.BoundAttribute[] = []; | ||||||
| 
 | 
 | ||||||
|     if (inputs.length || outputs.length) { |     if (inputs.length) { | ||||||
|  |       const EMPTY_STRING_EXPR = asLiteral(''); | ||||||
|  |       inputs.forEach(input => { | ||||||
|  |         if (input.type === BindingType.Animation) { | ||||||
|  |           // @attributes are for Renderer2 animation @triggers, but this feature
 | ||||||
|  |           // may be supported differently in future versions of angular. However,
 | ||||||
|  |           // @triggers should always just be treated as regular attributes (it's up
 | ||||||
|  |           // to the renderer to detect and use them in a special way).
 | ||||||
|  |           attrExprs.push(asLiteral(prepareSyntheticAttributeName(input.name)), EMPTY_STRING_EXPR); | ||||||
|  |         } else { | ||||||
|  |           nonSyntheticInputs.push(input); | ||||||
|  |         } | ||||||
|  |       }); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (nonSyntheticInputs.length || outputs.length) { | ||||||
|       attrExprs.push(o.literal(core.AttributeMarker.SelectOnly)); |       attrExprs.push(o.literal(core.AttributeMarker.SelectOnly)); | ||||||
|       inputs.forEach((i: t.BoundAttribute) => { attrExprs.push(asLiteral(i.name)); }); |       nonSyntheticInputs.forEach((i: t.BoundAttribute) => attrExprs.push(asLiteral(i.name))); | ||||||
|       outputs.forEach((o: t.BoundEvent) => { attrExprs.push(asLiteral(o.name)); }); |       outputs.forEach((o: t.BoundEvent) => attrExprs.push(asLiteral(o.name))); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     return attrExprs; |     return attrExprs; | ||||||
| @ -1429,3 +1453,7 @@ function isStyleSanitizable(prop: string): boolean { | |||||||
|   } |   } | ||||||
|   return false; |   return false; | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | function prepareSyntheticAttributeName(name: string) { | ||||||
|  |   return '@' + name; | ||||||
|  | } | ||||||
|  | |||||||
| @ -9,12 +9,13 @@ | |||||||
| import {ElementRef, TemplateRef, ViewContainerRef} from '@angular/core'; | import {ElementRef, TemplateRef, ViewContainerRef} from '@angular/core'; | ||||||
| import {RenderFlags} from '@angular/core/src/render3'; | import {RenderFlags} from '@angular/core/src/render3'; | ||||||
| 
 | 
 | ||||||
| import {RendererType2} from '../../src/render/api'; | import {RendererStyleFlags2, RendererType2} from '../../src/render/api'; | ||||||
| import {getOrCreateNodeInjectorForNode, getOrCreateTemplateRef} from '../../src/render3/di'; | import {getOrCreateNodeInjectorForNode, getOrCreateTemplateRef} from '../../src/render3/di'; | ||||||
| import {AttributeMarker, defineComponent, defineDirective, injectElementRef, injectTemplateRef, injectViewContainerRef} from '../../src/render3/index'; | import {AttributeMarker, defineComponent, defineDirective, injectElementRef, injectTemplateRef, injectViewContainerRef} from '../../src/render3/index'; | ||||||
|  | 
 | ||||||
| import {NO_CHANGE, bind, container, containerRefreshEnd, containerRefreshStart, element, elementAttribute, elementClassProp, elementContainerEnd, elementContainerStart, elementEnd, elementProperty, elementStart, elementStyleProp, elementStyling, elementStylingApply, embeddedViewEnd, embeddedViewStart, interpolation1, interpolation2, interpolation3, interpolation4, interpolation5, interpolation6, interpolation7, interpolation8, interpolationV, listener, load, loadDirective, projection, projectionDef, text, textBinding, template} from '../../src/render3/instructions'; | import {NO_CHANGE, bind, container, containerRefreshEnd, containerRefreshStart, element, elementAttribute, elementClassProp, elementContainerEnd, elementContainerStart, elementEnd, elementProperty, elementStart, elementStyleProp, elementStyling, elementStylingApply, embeddedViewEnd, embeddedViewStart, interpolation1, interpolation2, interpolation3, interpolation4, interpolation5, interpolation6, interpolation7, interpolation8, interpolationV, listener, load, loadDirective, projection, projectionDef, text, textBinding, template} from '../../src/render3/instructions'; | ||||||
| import {InitialStylingFlags} from '../../src/render3/interfaces/definition'; | import {InitialStylingFlags} from '../../src/render3/interfaces/definition'; | ||||||
| import {RElement, Renderer3, RendererFactory3, domRendererFactory3} from '../../src/render3/interfaces/renderer'; | import {RElement, Renderer3, RendererFactory3, domRendererFactory3, RText, RComment, RNode, RendererStyleFlags3, ProceduralRenderer3} from '../../src/render3/interfaces/renderer'; | ||||||
| import {HEADER_OFFSET, CONTEXT, DIRECTIVES} from '../../src/render3/interfaces/view'; | import {HEADER_OFFSET, CONTEXT, DIRECTIVES} from '../../src/render3/interfaces/view'; | ||||||
| import {sanitizeUrl} from '../../src/sanitization/sanitization'; | import {sanitizeUrl} from '../../src/sanitization/sanitization'; | ||||||
| import {Sanitizer, SecurityContext} from '../../src/sanitization/security'; | import {Sanitizer, SecurityContext} from '../../src/sanitization/security'; | ||||||
| @ -1387,7 +1388,7 @@ describe('render3 integration test', () => { | |||||||
|           } |           } | ||||||
|         }); |         }); | ||||||
|       } |       } | ||||||
|       const rendererFactory = new MockRendererFactory(); |       const rendererFactory = new ProxyRenderer3Factory(); | ||||||
|       new ComponentFixture(StyledComp, {rendererFactory}); |       new ComponentFixture(StyledComp, {rendererFactory}); | ||||||
|       expect(rendererFactory.lastCapturedType !.styles).toEqual(['div { color: red; }']); |       expect(rendererFactory.lastCapturedType !.styles).toEqual(['div { color: red; }']); | ||||||
|       expect(rendererFactory.lastCapturedType !.encapsulation).toEqual(100); |       expect(rendererFactory.lastCapturedType !.encapsulation).toEqual(100); | ||||||
| @ -1413,7 +1414,7 @@ describe('render3 integration test', () => { | |||||||
|           template: (rf: RenderFlags, ctx: AnimComp) => {} |           template: (rf: RenderFlags, ctx: AnimComp) => {} | ||||||
|         }); |         }); | ||||||
|       } |       } | ||||||
|       const rendererFactory = new MockRendererFactory(); |       const rendererFactory = new ProxyRenderer3Factory(); | ||||||
|       new ComponentFixture(AnimComp, {rendererFactory}); |       new ComponentFixture(AnimComp, {rendererFactory}); | ||||||
| 
 | 
 | ||||||
|       const capturedAnimations = rendererFactory.lastCapturedType !.data !['animations']; |       const capturedAnimations = rendererFactory.lastCapturedType !.data !['animations']; | ||||||
| @ -1435,11 +1436,74 @@ describe('render3 integration test', () => { | |||||||
|           template: (rf: RenderFlags, ctx: AnimComp) => {} |           template: (rf: RenderFlags, ctx: AnimComp) => {} | ||||||
|         }); |         }); | ||||||
|       } |       } | ||||||
|       const rendererFactory = new MockRendererFactory(); |       const rendererFactory = new ProxyRenderer3Factory(); | ||||||
|       new ComponentFixture(AnimComp, {rendererFactory}); |       new ComponentFixture(AnimComp, {rendererFactory}); | ||||||
|       const data = rendererFactory.lastCapturedType !.data; |       const data = rendererFactory.lastCapturedType !.data; | ||||||
|       expect(data.animations).toEqual([]); |       expect(data.animations).toEqual([]); | ||||||
|     }); |     }); | ||||||
|  | 
 | ||||||
|  |     it('should allow [@trigger] bindings to be picked up by the underlying renderer', () => { | ||||||
|  |       class AnimComp { | ||||||
|  |         static ngComponentDef = defineComponent({ | ||||||
|  |           type: AnimComp, | ||||||
|  |           consts: 1, | ||||||
|  |           vars: 1, | ||||||
|  |           selectors: [['foo']], | ||||||
|  |           factory: () => new AnimComp(), | ||||||
|  |           template: (rf: RenderFlags, ctx: AnimComp) => { | ||||||
|  |             if (rf & RenderFlags.Create) { | ||||||
|  |               element(0, 'div', [AttributeMarker.SelectOnly, '@fooAnimation']); | ||||||
|  |             } | ||||||
|  |             if (rf & RenderFlags.Update) { | ||||||
|  |               elementAttribute(0, '@fooAnimation', bind(ctx.animationValue)); | ||||||
|  |             } | ||||||
|  |           } | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |         animationValue = '123'; | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       const rendererFactory = new MockRendererFactory(['setAttribute']); | ||||||
|  |       const fixture = new ComponentFixture(AnimComp, {rendererFactory}); | ||||||
|  | 
 | ||||||
|  |       const renderer = rendererFactory.lastRenderer !; | ||||||
|  |       fixture.component.animationValue = '456'; | ||||||
|  |       fixture.update(); | ||||||
|  | 
 | ||||||
|  |       const spy = renderer.spies['setAttribute']; | ||||||
|  |       const [elm, attr, value] = spy.calls.mostRecent().args; | ||||||
|  | 
 | ||||||
|  |       expect(attr).toEqual('@fooAnimation'); | ||||||
|  |       expect(value).toEqual('456'); | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     it('should allow creation-level [@trigger] properties to be picked up by the underlying renderer', | ||||||
|  |        () => { | ||||||
|  |          class AnimComp { | ||||||
|  |            static ngComponentDef = defineComponent({ | ||||||
|  |              type: AnimComp, | ||||||
|  |              consts: 1, | ||||||
|  |              vars: 1, | ||||||
|  |              selectors: [['foo']], | ||||||
|  |              factory: () => new AnimComp(), | ||||||
|  |              template: (rf: RenderFlags, ctx: AnimComp) => { | ||||||
|  |                if (rf & RenderFlags.Create) { | ||||||
|  |                  element(0, 'div', ['@fooAnimation', '']); | ||||||
|  |                } | ||||||
|  |              } | ||||||
|  |            }); | ||||||
|  |          } | ||||||
|  | 
 | ||||||
|  |          const rendererFactory = new MockRendererFactory(['setAttribute']); | ||||||
|  |          const fixture = new ComponentFixture(AnimComp, {rendererFactory}); | ||||||
|  | 
 | ||||||
|  |          const renderer = rendererFactory.lastRenderer !; | ||||||
|  |          fixture.update(); | ||||||
|  | 
 | ||||||
|  |          const spy = renderer.spies['setAttribute']; | ||||||
|  |          const [elm, attr, value] = spy.calls.mostRecent().args; | ||||||
|  |          expect(attr).toEqual('@fooAnimation'); | ||||||
|  |        }); | ||||||
|   }); |   }); | ||||||
| 
 | 
 | ||||||
|   describe('element discovery', () => { |   describe('element discovery', () => { | ||||||
| @ -2201,7 +2265,7 @@ class LocalSanitizer implements Sanitizer { | |||||||
|   bypassSecurityTrustUrl(value: string) { return new LocalSanitizedValue(value); } |   bypassSecurityTrustUrl(value: string) { return new LocalSanitizedValue(value); } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| class MockRendererFactory implements RendererFactory3 { | class ProxyRenderer3Factory implements RendererFactory3 { | ||||||
|   lastCapturedType: RendererType2|null = null; |   lastCapturedType: RendererType2|null = null; | ||||||
| 
 | 
 | ||||||
|   createRenderer(hostElement: RElement|null, rendererType: RendererType2|null): Renderer3 { |   createRenderer(hostElement: RElement|null, rendererType: RendererType2|null): Renderer3 { | ||||||
| @ -2209,3 +2273,55 @@ class MockRendererFactory implements RendererFactory3 { | |||||||
|     return domRendererFactory3.createRenderer(hostElement, rendererType); |     return domRendererFactory3.createRenderer(hostElement, rendererType); | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | class MockRendererFactory implements RendererFactory3 { | ||||||
|  |   lastRenderer: any; | ||||||
|  |   private _spyOnMethods: string[]; | ||||||
|  | 
 | ||||||
|  |   constructor(spyOnMethods?: string[]) { this._spyOnMethods = spyOnMethods || []; } | ||||||
|  | 
 | ||||||
|  |   createRenderer(hostElement: RElement|null, rendererType: RendererType2|null): Renderer3 { | ||||||
|  |     const renderer = this.lastRenderer = new MockRenderer(this._spyOnMethods); | ||||||
|  |     return renderer; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | class MockRenderer implements ProceduralRenderer3 { | ||||||
|  |   public spies: {[methodName: string]: any} = {}; | ||||||
|  | 
 | ||||||
|  |   constructor(spyOnMethods: string[]) { | ||||||
|  |     spyOnMethods.forEach(methodName => { | ||||||
|  |       this.spies[methodName] = spyOn(this as any, methodName).and.callThrough(); | ||||||
|  |     }); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   destroy(): void {} | ||||||
|  |   createComment(value: string): RComment { return document.createComment(value); } | ||||||
|  |   createElement(name: string, namespace?: string|null): RElement { | ||||||
|  |     return document.createElement(name); | ||||||
|  |   } | ||||||
|  |   createText(value: string): RText { return document.createTextNode(value); } | ||||||
|  |   appendChild(parent: RElement, newChild: RNode): void { parent.appendChild(newChild); } | ||||||
|  |   insertBefore(parent: RNode, newChild: RNode, refChild: RNode|null): void { | ||||||
|  |     parent.insertBefore(newChild, refChild, false); | ||||||
|  |   } | ||||||
|  |   removeChild(parent: RElement, oldChild: RNode): void { parent.removeChild(oldChild); } | ||||||
|  |   selectRootElement(selectorOrNode: string|any): RElement { | ||||||
|  |     return ({} as any); | ||||||
|  |   } | ||||||
|  |   setAttribute(el: RElement, name: string, value: string, namespace?: string|null): void {} | ||||||
|  |   removeAttribute(el: RElement, name: string, namespace?: string|null): void {} | ||||||
|  |   addClass(el: RElement, name: string): void {} | ||||||
|  |   removeClass(el: RElement, name: string): void {} | ||||||
|  |   setStyle( | ||||||
|  |       el: RElement, style: string, value: any, | ||||||
|  |       flags?: RendererStyleFlags2|RendererStyleFlags3): void {} | ||||||
|  |   removeStyle(el: RElement, style: string, flags?: RendererStyleFlags2|RendererStyleFlags3): void {} | ||||||
|  |   setProperty(el: RElement, name: string, value: any): void {} | ||||||
|  |   setValue(node: RText, value: string): void {} | ||||||
|  | 
 | ||||||
|  |   // TODO(misko): Deprecate in favor of addEventListener/removeEventListener
 | ||||||
|  |   listen(target: RNode, eventName: string, callback: (event: any) => boolean | void): () => void { | ||||||
|  |     return () => {}; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user