4

I have an Angular component that depends on a click event that occurs on the root App component. The child is located in random places inside the <app> component, hence why I didn't list it.

@Component({
   template: '<div>Child!</div>'
})
export class Child {
   constructor () {}
}

@Component({
   selector: 'app',
   template: '<div (click)=foo()></div>'
})
export class App {
   rootClickEmitter = new EventEmitter();
   foo () {
      this.rootClickEmitter.emit('bar');
   }
}

How can I have the child component receive the rootClickEmitter event?

pah
  • 4,700
  • 6
  • 28
  • 37
Jack Guy
  • 8,346
  • 8
  • 55
  • 86
  • Put your EventEmitter into a service instead. Inject the service into your App and the child/descendant. The App will call emit() on the service's EventEmitter, and your child component will subscribe() to it. – Mark Rajcok Jan 27 '16 at 03:29
  • I'm not sure its the best approach to share emitters, but you can just add an `@Input` property of type `EventEmitter` in the Child and receive it as a parameter: `` – Langley Jan 27 '16 at 03:29
  • @Langley I'm trying to figure out the best way to get the event for a click outside of my child component. I figured the most "Angularly" way of doing it would just be to bind that event to the root component and send it down to the child which then checks if it's a target. Do you have another recommendation? – Jack Guy Jan 27 '16 at 03:36
  • imho you usually want to bubble up events, not bubble them down, and even this should be rare. What do you want to happen when the parent gets clicked? I wonder if what you need is a model shared between both components and have the parent change that model when clicked. – Langley Jan 27 '16 at 03:46
  • @Langley It's just a custom dropdown component. Needs to collapse whenever there's a click outside of it. I considered trying to tie it to a native select element but that became really cumbersome really quickly. – Jack Guy Jan 27 '16 at 03:57
  • why don't you set the method that collapses your drowdown as a click handler on the document object? Something like: `$(document).on("click","body",function() { closeTheThing(); });` – Langley Jan 27 '16 at 04:14
  • @Langley I thought "document" was to be avoided in Angular 2 at all costs to avoid breaking server-side rendering. – Jack Guy Jan 27 '16 at 04:16
  • For what I undestand what you need to avoid is the DOM for creating/manipulating objects yourself, you should allow angular to do this through the virtual DOM, but in this case you are only adding a listener to the whole page, if you listen to the parent's component, its just the parent's area, not the whole body right? – Langley Jan 27 '16 at 04:26

1 Answers1

2

For "click outside", try this:

@Component({
  selector: 'child',
  template: '<div (click)="childClick($event)">child content here</div>',
  host: { '(document:click)': 'docClick()' }
})
export class Child {
  docClick() {
    console.log('clicked outside');
  }
  childClick(ev) {
    console.log('child click');
    ev.stopPropagation();
  }
}

Plunker

Update: Eric just posted the following in another answer:

"You can use listenGlobal that will give you access to document, body, etc.

renderer.listenGlobal('document', 'click', () => {
  console.log('Document clicked');
});
Community
  • 1
  • 1
Mark Rajcok
  • 362,217
  • 114
  • 495
  • 492