user2302244
user2302244

Reputation: 935

Stenciljs custom event not responding via @Listen

I am trying to understand the flow of the custom event emitter. I have the rolling code where the mouse events work but not the custom events. Tracing it through dev tools, it is emitting but not getting picked up by the listener.

The top-level component is here:

import { Component, Prop, Listen, State, Event, EventEmitter } from "@stencil/core"

@Component ({
    tag: "control-comp"
})
export class  SmsComp1 {
    @Prop() compTitle:string;
    @State() stateData: object = {name: "Fred"};

    @Event() stateChanged: EventEmitter;

    @Listen('inBox')
    inBoxHandler(ev) {
        console.log('In box', ev);
        this.stateData["name"] = ev.name;
        console.log('Emitting')
        this.stateChanged.emit(this.stateData);   
    }

    render () {
        let index = [1, 2, 3, 4, 5]
        return (
            <div>
                <h1>{this.compTitle}</h1>
                {index.map( (i) => {
                    return <my-component first={i.toString()} last="Don't call me a framework" width={i*40} height={i*40}></my-component>
                })} 
                <my-component first={this.stateData["name"]} last="'Don't call me a framework' JS"></my-component>
            </div>
        )
    }
} 

The component is here:

import { Component, Prop, Listen, State, Event, EventEmitter } from '@stencil/core';

@Component({
  tag: 'my-component',
  styleUrl: 'my-component.css',
  shadow: true
})
export class MyComponent {

  @Prop() first: string;
  @Prop() last: string;
  @Prop() width: number = 120;
  @Prop() height: number = 100;
  @State() colour: string = 'red';

  @Event() inBox: EventEmitter;

  @Listen('mouseover') 
  clickHandler() {
    this.colour = 'white';
    this.inBox.emit({action: 'IN_BOX',
                    name: this.first+' '+this.last})
  }

  @Listen('mouseout')
  mouseOutHandler() {
    this.colour = 'red';
  }

  @Listen('stateChanged')
  stateChangedHandler(state) {
    console.log('Received', state);
  }

  render() {
    return (
        <svg width={this.width+10} height={this.height+10}>
          <rect width={this.width} height={this.height} fill='green'></rect>
          <circle cx={this.width/2} cy={this.height/2} r={this.width*0.1} fill={this.colour}></circle>
          <text fill='white' x='10' y='10'>{this.first+' '+this.last}</text>
        </svg>
    );
  }
}

Finally the index.html is here:

<!DOCTYPE html>
<html dir="ltr" lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=5.0">
  <title>Stencil Component Starter</title>
  <script src="/build/mycomponent.js"></script>

</head>
<body>

 <control-comp compTitle="Stencil Example"></control-comp>
  <my-component first="My Dead" last='Component' width=100 height=120></my-component>

</body>
</html>

Can you suggest why the stateChanged event is not being noticed by my-component?

Upvotes: 0

Views: 7217

Answers (2)

Lukas Kirner
Lukas Kirner

Reputation: 4339

Maybe it's to late but you can use the options for @Listen

export interface ListenOptions {
  target?: 'parent' | 'body' | 'document' | 'window';
  capture?: boolean;
  passive?: boolean;
}

(Source: https://stenciljs.com/docs/events#listen-s-options)

If you attach the Listener to document you will get the expected event

@Listen('inBox', { target: 'document' })
...

Upvotes: 6

matthewsteele
matthewsteele

Reputation: 1862

Stencil events, like other CustomEvents only bubble up a component tree, not down.

Since my-component is a child of control-comp, the parent's stateChanged event can't seen by control-comp.

You'll want to find another approach to have a parent communicate to a child component. The "standard" way to do this is to set a @Prop and maybe a @Watch on the child, and update the prop in the parent's render() function.

Or, you could use a more robust approach like stencil-redux or stencil-state-tunnel.

Upvotes: 3

Related Questions