-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
17 changed files
with
442 additions
and
31 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,16 @@ | ||
import { NgModule } from '@angular/core'; | ||
import { ContribRenderExtensionModule } from '@angular-contrib/core'; | ||
import { NgDynamic } from './ng-dynamic'; | ||
|
||
@NgModule({ | ||
declarations: [NgDynamic], | ||
exports: [NgDynamic], | ||
imports: [ | ||
ContribRenderExtensionModule, | ||
], | ||
declarations: [ | ||
NgDynamic, | ||
], | ||
exports: [ | ||
NgDynamic, | ||
], | ||
}) | ||
export class ContribNgDynamicModule {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export * from './render-extension.module'; |
20 changes: 20 additions & 0 deletions
20
projects/core/src/render-extension/render-extension.module.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
import { NgModule } from '@angular/core'; | ||
import { RENDER_INTERCEPTORS } from '../render-intercept/render-intercept'; | ||
import { ContribRenderInterceptModule } from '../render-intercept/render-intercept.module'; | ||
import { RendererExtensionInterceptor } from './render-extension'; | ||
|
||
declare module '@angular/core' { | ||
interface Renderer2 { | ||
childNodes(node: Node): NodeList; | ||
} | ||
} | ||
|
||
@NgModule({ | ||
imports: [ | ||
ContribRenderInterceptModule, | ||
], | ||
providers: [ | ||
{ provide: RENDER_INTERCEPTORS, multi: true, useClass: RendererExtensionInterceptor }, | ||
] | ||
}) | ||
export class ContribRenderExtensionModule {} |
39 changes: 39 additions & 0 deletions
39
projects/core/src/render-extension/render-extension.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
import { Renderer2, Component, ElementRef } from '@angular/core'; | ||
import { TestBed, ComponentFixture } from '@angular/core/testing'; | ||
import { ContribRenderExtensionModule } from './render-extension.module'; | ||
|
||
describe('Renderer extension', () => { | ||
let fixture: ComponentFixture<TestComponent>; | ||
let component: TestComponent; | ||
|
||
beforeEach(async () => { | ||
await TestBed.configureTestingModule({ | ||
imports: [ContribRenderExtensionModule], | ||
declarations: [TestComponent], | ||
}).compileComponents(); | ||
}); | ||
|
||
beforeEach(() => { | ||
fixture = TestBed.createComponent(TestComponent); | ||
component = fixture.componentInstance; | ||
}); | ||
|
||
it('should support childNodes', () => { | ||
expect(component.childNodes.length).toBe(3); | ||
}); | ||
|
||
}); | ||
|
||
@Component({ | ||
template: `<p><p><p>`, | ||
}) | ||
class TestComponent { | ||
constructor( | ||
private host: ElementRef, | ||
private renderer: Renderer2, | ||
) {} | ||
|
||
get childNodes() { | ||
return this.renderer.childNodes(this.host.nativeElement); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import { RenderInterceptor } from '../render-intercept/render-intercept'; | ||
|
||
export class RendererExtensionInterceptor implements RenderInterceptor { | ||
childNodes(node: Node): NodeList { | ||
return node.childNodes; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
# Renderer Interception | ||
|
||
Intercepting the render process. | ||
|
||
## Type | ||
|
||
**Provider** | ||
|
||
## Provenance | ||
|
||
+ https://github.com/angular/angular/issues/3929 | ||
|
||
## NgModule | ||
|
||
`@angular-contrib/core#ContribRenderInterceptModule` | ||
|
||
## Usage | ||
|
||
```typescript | ||
import { ContribRenderInterceptModule, RenderInterceptor, RENDER_INTERCEPTORS } from '@angular-contrib/core'; | ||
|
||
class LinkSpyInterceptor implements RenderInterceptor { | ||
setAttribute(el: Element, name: string, value: string, namespace: string | undefined, renderer: Renderer2) { | ||
if (el.tagName === 'A' && name === 'href') { | ||
console.log(`New link found: ${value}`); | ||
} | ||
return renderer.setAttribute(el, name, value, namespace); | ||
} | ||
} | ||
|
||
@NgModule({ | ||
imports: [ ContribRenderInterceptModule ], | ||
providers: [ | ||
{ provide: RENDER_INTERCEPTORS, multi: true, useClass: LinkSpyInterceptor }, | ||
] | ||
}) | ||
class MyModule { } | ||
``` | ||
|
||
## Requirements | ||
|
||
+ ContribRenderInterceptModule |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
export * from './render-intercept'; | ||
export * from './render-intercept.module'; |
68 changes: 68 additions & 0 deletions
68
projects/core/src/render-intercept/render-intercept.module.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
import { NgModule, RendererFactory2, Renderer2, RendererType2, Inject, ComponentFactoryResolver, Injector } from '@angular/core'; | ||
import { RENDER_INTERCEPTORS, RenderInterceptor, createInterceptingRenderer, RendererRetriever } from './render-intercept'; | ||
|
||
declare module '@angular/core' { | ||
interface Renderer2 { | ||
isExtended?: boolean; | ||
} | ||
} | ||
|
||
function throwMissingChildNodes(): never { | ||
throw new Error(`Renderer2#childNodes not implemented, did you forget to import ContribRenderExtensionModule?`); | ||
} | ||
|
||
function patchRenderer(renderer: Renderer2): void { | ||
if (renderer.isExtended == null) { | ||
renderer.isExtended = true; | ||
} | ||
|
||
if (renderer.childNodes == null) { | ||
renderer.childNodes = throwMissingChildNodes; | ||
} | ||
} | ||
|
||
function patchExoticRenderer(renderer: Renderer2, delegate: Renderer2): void { | ||
renderer.childNodes = (node: Node) => delegate.childNodes(node); | ||
} | ||
|
||
@NgModule({ | ||
declarations: [ | ||
RendererRetriever, | ||
], | ||
entryComponents: [ | ||
RendererRetriever, | ||
], | ||
}) | ||
export class ContribRenderInterceptModule { | ||
private patchedFactories = new WeakSet<RendererFactory2>(); | ||
|
||
constructor( | ||
injector: Injector, | ||
cfr: ComponentFactoryResolver, | ||
rendererFactory: RendererFactory2, | ||
@Inject(RENDER_INTERCEPTORS) interceptors: RenderInterceptor[], | ||
) { | ||
if (!this.patchedFactories.has(rendererFactory)) { | ||
const originalCreateRenderer = rendererFactory.createRenderer; | ||
rendererFactory.createRenderer = function(hostElement: Node, type: RendererType2 | null): Renderer2 { | ||
const originalRenderer = originalCreateRenderer.call(this, hostElement, type); | ||
patchRenderer(originalRenderer); | ||
return createInterceptingRenderer(originalRenderer, interceptors); | ||
}; | ||
this.patchedFactories.add(rendererFactory); | ||
} | ||
|
||
const extendedRenderer = rendererFactory.createRenderer(null, null); | ||
const host = extendedRenderer.createElement('div'); | ||
const componentFactory = cfr.resolveComponentFactory(RendererRetriever); | ||
const { renderer } = componentFactory.create(injector, undefined, host).instance; | ||
|
||
// DebugRenderer2 maybe in use, require manual patching | ||
if (renderer.isExtended == null) { | ||
const proto = Object.getPrototypeOf(renderer); | ||
if (proto !== Object.prototype) { | ||
patchExoticRenderer(proto, extendedRenderer); | ||
} | ||
} | ||
} | ||
} |
52 changes: 52 additions & 0 deletions
52
projects/core/src/render-intercept/render-intercept.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
import { Renderer2, Component } from '@angular/core'; | ||
import { TestBed, ComponentFixture } from '@angular/core/testing'; | ||
import { RenderInterceptor, RENDER_INTERCEPTORS } from './render-intercept'; | ||
import { ContribRenderInterceptModule } from './render-intercept.module'; | ||
|
||
describe('Renderer interception', () => { | ||
let fixture: ComponentFixture<TestComponent>; | ||
let component: TestComponent; | ||
let interceptor: RenderInterceptor; | ||
|
||
beforeEach(() => { | ||
interceptor = { | ||
setAttribute(el: Element, name: string, value: string, namespace: string | undefined, renderer: Renderer2) { | ||
if (value === 'foo') { | ||
value = 'bar'; | ||
} | ||
return renderer.setAttribute(el, name, value, namespace); | ||
}, | ||
createText(value: string, renderer: Renderer2) { | ||
if (value === 'ABC') { | ||
value = 'DEF'; | ||
} | ||
return renderer.createText(value); | ||
} | ||
}; | ||
}); | ||
|
||
beforeEach(async () => { | ||
await TestBed.configureTestingModule({ | ||
imports: [ContribRenderInterceptModule], | ||
declarations: [TestComponent], | ||
providers: [ | ||
{ provide: RENDER_INTERCEPTORS, multi: true, useValue: interceptor }, | ||
], | ||
}).compileComponents(); | ||
}); | ||
|
||
beforeEach(() => { | ||
fixture = TestBed.createComponent(TestComponent); | ||
component = fixture.componentInstance; | ||
}); | ||
|
||
it('should apply RenderInterceptor', () => { | ||
expect(fixture.nativeElement.innerHTML).toBe(`<article id="bar">DEF</article>`); | ||
}); | ||
|
||
}); | ||
|
||
@Component({ | ||
template: `<article id="foo">ABC</article>`, | ||
}) | ||
class TestComponent {} |
Oops, something went wrong.