0

I am using stencil framework in my project to create components. Project structure is tree base means subcompacts are having sub components as well. I have used custom events at many places.

My concern is when I listen events with parameter target:body, code is executed twice as I have used same component twice on single page. I have created sample component to show the issue/concern.

Main component code snippet

import { Event, EventEmitter, Component, Prop, h } from '@stencil/core';
@Component({
   tag: 'my-component'
})
export class MyComponent {
  @Prop() first: string;
  @Prop() middle: string;
  @Prop() last: string;
  @Event() nameClick: EventEmitter;

handleClickEvent(e): void {
 this.nameClick.emit({ name: `${this.first} ${this.middle} ${this.last}` });
}

render() {
   return (
     <div>
       <div onClick={(e) => this.handleClickEvent(e)}><u>Click here to show you name!!</u></div>
       <p><div>Your full name</div><sub-component></sub-component></p>
     </div>
   )
 }
}

Sub component snippet

import { Component, h, Listen, State } from '@stencil/core';
@Component({
  tag: 'sub-component'
})
export class SubComponent {

@State() fullName: string;

@Listen('nameClick', { target: 'body' })
onNameClick(event: CustomEvent) {
    this.fullName = event.detail.name;
}
render() {
    return <input type="text" value={this.fullName}></input>
 }
}

Html page where component is used

<div id="content" class="tabcontent">
 <div style="width:50%;float:left">
  <p>Component 1</p>
  <my-component first="Stencil" middle="web" last="component"></my-component>
 </div>
 <div style="width:50%;float:right">
  <p>Component 2</p>
  <my-component first="Stencil" middle="web" last="component"></my-component>
 </div>
</div>

It seems this code is culprit to listen event twice @Listen('nameClick', { target: 'body' }).

Gif for demo

Thanks in advance for a help!!

Urvesh86
  • 31
  • 4

2 Answers2

0

Using target: body for this kind of thing is a lot like listening for a click on a button from body which will be triggered for every click - it's just not the right way to approach the problem.

Your parent my-component already knows which sub-component is which because it is part of the template, so it can update directly if you make fullName a @Prop.

For example (partial code):

export class SubComponent {

  @Prop() fullName: string;

  render() {
    return <input type="text" value={this.fullName}></input>
  }
}

export class MyComponent {
  ...
  private _childComponent: HTMLSubComponentElement;

  handleClickEvent(e): void {
    this._childComponent.fullName = `${this.first} ${this.middle} ${this.last}`;
  }

  render() {
    return (
      <div>
        <div onClick={(e) => this.handleClickEvent(e)}><u>Click here to show you name!!</u></div>
        <p>
          <div>Your full name</div>
          <sub-component ref={el => this._childComponent = el}></sub-component>
        </p>
      </div>
    );
  }
}

If you don't want to use a @Prop - you could also define a @Method to set the fullName value.

G. Tranter
  • 16,766
  • 1
  • 48
  • 68
0

If it is Parent and Child Component, you don't need to attach the target to body.

@Listen('nameClick', {})
onNameClick(event: CustomEvent){
   this.fullName = event.detail.name;
}
SCX
  • 1
  • 1