Search code examples
angulareventsnativescript

How to know which child sending event to parent with @Output on Angular


I lost my mind when trying to get "current native element" on an event with Angular (11) and NativeScript...

I have a container (home component) that contains multiple blocks (block component). Each component have a "onPan" event management, and this event is forward with @Output to let the parent known. Then, the parent (home) listen this pan event...

All of this works. But I can't check WHICH element is on move when the parent get the event. I mean, the "PanGestureEventData" carries a native "view" property, but I wasn't able to retrieve the Angular "nativeElement" from this event.

I tried to add "this" on parent template listener, but I don't know which object it is :x

Did you have any solution to retrieve, on parent, which element from "blocks" QueryList is currently sending the event ?

It's a general case, it could be any custom event that doesn't carry the current target, sent from a child to its parent :)

Thanks !

My snippets :

home.component.html

<GridLayout #refGrid columns="*,*,*,*,*,*" rows="*,*,*,*,*,*" width="100%">
  <ns-block #block col="1" row="1" (moveEvent)="onMove(this, $event)"></ns-block>
  <ns-block #block col="2" row="1" (moveEvent)="onMove(this, $event)"></ns-block>
  <ns-block #block col="3" row="1" (moveEvent)="onMove(this, $event)"></ns-block>
  <ns-block #block col="1" row="2" (moveEvent)="onMove(this, $event)"></ns-block>
</GridLayout>

home.component.ts

import {Component, ElementRef, QueryList, ViewChildren} from "@angular/core";
import {PanGestureEventData} from "@nativescript/core";
import {BlockComponent} from "../component/block/block.component";

@Component({
  selector: "Home",
  templateUrl: "./home.component.html"
})
export class HomeComponent {
  @ViewChildren(BlockComponent, { read: ElementRef }) blocks!: QueryList<ElementRef>;

  onMove(element, args: PanGestureEventData): void {
    console.log("move", element);
  }
}

block.component.tns.html

<Image (pan)="onPan($event)"
   src="https://images.pexels.com/photos/102104/pexels-photo-102104.jpeg?auto=compress&cs=tinysrgb&dpr=1&w=500"></Image>

block.component.ts

import {Component, Output, EventEmitter} from '@angular/core';
import {PanGestureEventData} from "@nativescript/core";

@Component({
    selector: 'ns-block',
    templateUrl: './block.component.html',
    styleUrls: ['./block.component.css']
})
export class BlockComponent {

    @Output() moveEvent = new EventEmitter<PanGestureEventData>();

    onPan(args: PanGestureEventData): void {
        this.moveEvent.emit(args);
    }
}

Solution

  • Couldn't you give different template ref variables to each child component and send it to the event handler?

    home.component.html

    <GridLayout #refGrid columns="*,*,*,*,*,*" rows="*,*,*,*,*,*" width="100%">
      <ns-block #block1 col="1" row="1" (moveEvent)="onMove(block1, $event)"></ns-block>
      <ns-block #block2 col="2" row="1" (moveEvent)="onMove(block2, $event)"></ns-block>
      <ns-block #block3 col="3" row="1" (moveEvent)="onMove(block3, $event)"></ns-block>
      <ns-block #block4 col="1" row="2" (moveEvent)="onMove(block4, $event)"></ns-block>
    </GridLayout>
    

    home.component.ts

    onMove(element, args: PanGestureEventData): void {
      console.log("move", element);
    }
    

    Update: dynamically generated <ns-block>

    In case of a dynamically generated markup using a loop, it becomes more simple since you don't have to individually name the tags. You could directly the current element to the handler using it's template ref variable.

    <GridLayout #refGrid columns="*,*,*,*,*,*" rows="*,*,*,*,*,*" width="100%">
      <ns-block *ngFor="let item of someArray" #block col="1" row="1" (moveEvent)="onMove(block, $event)"></ns-block>
    </GridLayout>