20

The Problem

Normally, in Angular 2, one would listen to an event via the following syntax:

<my-elem (customEvent)="customEventProcessor"></my-elem>

But when I use a router, that host - <my-elem> - does not exist in any template. Instead, there's a <router-outlet>, and my component is loaded upon navigation. Thus, the crux of my problem is, How can I force my host to listen to my custom event without relying on a template?

Optional Details

Suppose I have some element list-view, which is a child of my root component. list-view listens for a custom event via the normal syntax:

<list-view (customEvent)="customEventProcessor()"></list-view>

Just for completeness, the list-view component that emits the event also has a predictable structure:

<button (click)="onDetailsClick(propertyOfInterest)">Click here</button>

The list-view sends the event up to the parent via observation.

class ListView {

    ...

        public onDetailsClick(property: string): void {

            this.customEvent.next({ value: property });
    
        }

}

and that event triggers the customEventProcessor() function. So far so good. However, when I use a router to control whether list-view is present, I cannot (to my knowledge) insert a command to monitor some event.

I am not sure what the best approach is to handle this case.

3
  • You can set a template for your routes, so that shouldn't change really, probably I misunderstood your problem. Anyway, to mimic the HTML way, to emit you can use @Output / outputs : [] and to listen you can use @HostListener / host : {} Commented Nov 15, 2015 at 13:22
  • @EricMartinez I don't think your proposal works. As far as I can tell, custom events cannot bubble up to a parent component. If you have an example of how to make a router listen to a custom event, I'm all ears - but I've had no luck here. A demo would be nice. Commented Nov 15, 2015 at 20:59
  • No, it doesn't work, I got your issue wrong :D Commented Nov 16, 2015 at 1:09

1 Answer 1

8

This problem isn't resolved yet (see this github issue). Here is one of the possible solutions at this moment (see this plunk):

@Component({
  selector: 'my-app',
  directives: [RouterOutlet, RouterLink],
  template: `
    <h1>{{ message }}</h1>
    <a [router-link]="['./MyElem1']">MyElem1</a>
    <a [router-link]="['./MyElem2']">MyElem2</a>
    <router-outlet></router-outlet>
  `
})
@RouteConfig([
  { path: '/', redirectTo: '/my-elem1' },
  { path: '/my-elem1', name: 'MyElem1', component: MyElem1 },
  { path: '/my-elem2', name: 'MyElem2', component: MyElem2 },
])
export class App {
  message: string = 'Click on the button';

  @ViewChild(MyElem1) myElem1: MyElem1;
  @ViewChild(MyElem2) myElem2: MyElem2;

  constructor(router: Router) {
    let subs = null;
    router.subscribe(() => {
      if (subs) { subs.unsubscribe(); subs = null; }

      if (this.myElem1) {
        subs = this.myElem1.customEvent1.subscribe(m=>this.processCustomEvent(m));
      }
      if (this.myElem2) {
        subs = this.myElem2.customEvent2.subscribe(m=>this.processCustomEvent(m));
      }
    });
  }

  processCustomEvent(message) { this.message = message }
}
Sign up to request clarification or add additional context in comments.

5 Comments

So there are two, separate problems: 1) custom events can't bubble to a parent component, and 2) there are no parent-child bindings in a router. The link you provided seems to handle 2) but not 1). Either way, your solution does seem to fix my problem.
See also. Point here is, injecting another router into a child component does not create a new router - it's a singleton. As such, there's little overhead in just using a the childRouter scoped to the child component.
@Jefftopia, events are supposed to bubble up to parent components. To handle them, you need to use the carat: (^click)="parentFunction()" egghead.io/lessons/angular-2-angular-2-custom-events-es5. but getting the events without the html does seem to be an issue.
@alexpods just curious, any updates for your answer now that we're out of beta?
just to help future users, @Jefftopia to your problem 1 - "custom events can't bubble to a parent component" or "force my host to listen to my custom event without relying on a template?" Angular now offer two approaches 1. Parent calls a ViewChild "angular.io/docs/ts/latest/cookbook/…" 2. Parent and children communicate via a service "angular.io/docs/ts/latest/cookbook/…"

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.