6

I want to access the current element where the pipe is being used in. Is it possible in Angular?

Pipe is used as follows e.g. <input type="text" value="'my-translation-key' | translate" />

Here is an example of the actual pipe

@Pipe({
  name: 'translate',
  pure: false
})
export class TranslatePipe implements PipeTransform {
  transform(key: string, params?: ITranslationSelector): string {
      // 1. Get the value by key
      // 2. Return the value

      // What I want as well:
      // this.el.nativeElement.dataset.translationKey = key;
      // Where el is current element where pipe is used
  }
}
br.julien
  • 3,420
  • 2
  • 23
  • 44
Stan
  • 25,744
  • 53
  • 164
  • 242

3 Answers3

4

You can also use a template reference variable. Template:

<input #inputEl type="text" value="'my-translation-key' | translate: inputEl" />

The part of the Pipe ts file:

export class TranslatePipe implements PipeTransform {
    transform(key: string, element?: any): string {
        // Element was marked by #inputEl template variable.
        element.dataset.translationKey = key;
    }
}
Chenna
  • 2,383
  • 3
  • 20
  • 36
József Cserkó
  • 299
  • 2
  • 9
3

Two possible solutions based on your feasibility:

  1. Create Template reference variable and send it as param to your custom Pipe.

Where custom Pipe is

export class CustomPipe implements PipeTransform {
    transform(key: string, params: any): string {
        //Here param is your input element
        return 'your_value';
    }

But in this approach, you are getting input element in custom pipe as HTML element which may not fullfill your requirement.

  1. Fetch Input Element as ViewChild in your component then pass it to custom pipe

    @ViewChild('inputRef') inputElementRef:ElementRef;

In this custom pipe will be :

export class CustomPipe implements PipeTransform {
        transform(key: string, params: any): string {
            //Here param is your input element
            return 'your_value';
        }

By this, you can work easily but this approach is better if you have only one or two elements in a single template as fetching multiple View Child won't be a clean code though it'll work fine.

Ankit Kapoor
  • 1,615
  • 12
  • 18
2

I would like to share some attempts which did not lead me to the desired solution, but maybe could lead someone else to get it working. Also another idea which was not mentioned, yet.


Hook into the templating pipeline

Another way could be to hook into the template creating pipeline of angular.

Idea: Get the AST and check for the pipe class and inject the reference of the element and put it in as a parameter (like mentioned before from others).

For example like these guys did it: https://github.com/typebytes/ngx-template-streams


Attempts:

With pure: true there is only one instance pipe class created per component.

With pure: false there is one instance pipe class created per each use of the pipe in your components HTML.

You can inject elementRef: ElementRef in the constructor of the pipe, but you will only get the reference to the component in which the pipe is used in (in both cases).

I thought maybe it could work somehow with another directive being injected into the pipe which has the reference to the HTMLElement.

Example:

<my-component>
    <div>{{'foo' | mypipe}}</div>
    <div>{{'foo' | mypipe}}</div>
    <input type="text" [value]="'bar' | mypipe">
    <input type="text" [value]="'bar' | mypipe">
</my-component>

I tried to create a directive to get a reference for usual HTMLElements which looked the following:

@Directive({
    selector: 'input,div,span'
})
export class ElementRefDirective {
    constructor(public readonly elementRef: ElementRef) {}
}

But I could not get it injected into the MyPipe class, nor the other way around (getting the MyPipe injected into the directive).

Stefan Rein
  • 8,084
  • 3
  • 37
  • 37