Skip to content

Instantly share code, notes, and snippets.

@bennadel
Created May 6, 2018 19:45
Experiment: Injecting A Component Reference Into A Pipe Instance In Angular 6.0.0
// Import the core angular services.
import { Component } from "@angular/core";
// Import the application components and services.
import { FnPipeContext } from "./fn.pipe";
// ----------------------------------------------------------------------------------- //
// ----------------------------------------------------------------------------------- //
@Component({
selector: "my-app",
// Here, we can provide special services that are available in the component
// injector. In this case, we're telling the FnPipe to use the AppComponent instance
// as the context when executing the "fn" function reference.
viewProviders: [
{
provide: FnPipeContext,
useClass: AppComponent
}
],
styleUrls: [ "./app.component.less" ],
template:
`
<p>
<a (click)="message = 'hello world';">Use message one</a>.
<br />
<a (click)="message = 'what it be like';">Use message two</a>.
</p>
<p>
Pipe output: <strong>{{ message | fn:formatMessage }}</strong>
</p>
`
})
export class AppComponent {
public message: string;
// I initialize the app-component.
constructor() {
this.message = "";
}
// ---
// PUBLIC METHODS.
// ---
// I am the function being invoked by the FnPipe.
public formatMessage( value: string ) : string {
// We can use the "this" reference here because we are providing the AppComponent
// as the FnPipeContext token to the local Injector. Its existence will get the
// FnPipe to execute the function reference in the current component context.
// --
// NOTE: As always, we could have used the Fat-Arrow notation (=>) to bind this
// function to the AppComponent instance, which would obviate the need for the
// FnPipeContext token.
return( `Context[ ${ this.constructor.name } ] => ${ value }` );
}
}
// Import the core angular services.
import { Optional } from "@angular/core";
import { Pipe } from "@angular/core";
import { PipeTransform } from "@angular/core";
import { Self } from "@angular/core";
// ----------------------------------------------------------------------------------- //
// ----------------------------------------------------------------------------------- //
// I provide a dependency-injection token for the Fn pipe execution context.
export class FnPipeContext {
// ...
}
@Pipe({
name: "fn",
pure: true
})
export class FnPipe implements PipeTransform {
private context: any;
// I initialize the fn-pipe.
// --
// NOTE: We are injecting an OPTIONAL context for function execution.
constructor( @Optional() @Self() context: FnPipeContext ) {
this.context = context || null;
}
// ---
// PUBLIC METHODS.
// ---
// I pass the first and rest arguments to the given function reference. This pipe
// is designed to be used in a template to access a component method:
// --
// In a template: {{ valueA | fn : componentMethodRef : valueB }}
// --
// ... becomes the invocation: context.componentMethodRef( valueA, valueB ).
public transform(
headArgument: any,
fnReference: Function,
...tailArguments: any[]
) : any {
// Due to the way pipes receive arguments, we can have inputs on both sides of
// the function reference. As such, let's join the two input sets when invoking
// the given Function reference.
return( fnReference.apply( this.context, [ headArgument, ...tailArguments ] ) );
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment