26

I'm build a webapp using Angular4 and I'm trying to create a button to call functions on other components.

I was reading other similar questions here, but they have a scenario where the components are child or sibling components with the declaration on the html, e.g.:

<parent-component>
    <child-component></child-component>
    <sibling-component></sibling-component>
</parent-component>

However, I'm working on a different scenario, because my components are Invoked via routing and I can't make it work.

Basically I have a header on the page with some controls to execute functions, but these functions need to trigger/change data on other components loaded via the routing.

For example, I have the routing /store to show a list of stores, I also have a drawer on this page that I want to show when the user clicks on a button on the header, this is the problem, because I can't propagate the event from the HeaderComponent to the StoreComponent.

These are my files:

header.component.ts

@Component({
    selector: 'header',
    templateUrl: `
        <section class="container">
            <button (click)="clickFilter()">Open filter</button>
        </section>
    `
})

export class HeaderComponent {
    @Output() onFilter: EventEmitter = new EventEmitter();

    clickFilter():void {
        this.onFilter.emit('Register click');
    }
}

store.component.ts

@Component({
    templateUrl: 'store.html'
})

export class StoreComponent {
    onFilterClick(event) {
        console.log('Fire onFilterClick: ', event);
    }
}

// store.html
<article class="card" (onFilter)="onFilterClick($event)">
    Test
</article>

But this is not working.

Also, I don't actually need to call a function, I could just pass a boolean value, for example, to bind to a property on the StoreComponent and then toggle the div inside the html file.

2
  • It's not totally clear how these components are arranged in the app. stackoverflow.com/help/mcve would help. The components that are not in parent-child relationship can communicate either through common parent or through a service on parent injector. If communicating through parent is not an option... do the math. Commented Jul 25, 2017 at 12:58
  • angular.io/guide/component-interaction service approach Commented Jul 25, 2017 at 13:00

2 Answers 2

45

I didn't get time to test it but similar solution worked for me. the code created for your need.

Create a service like this

import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import { Subject } from 'rxjs/Subject';

@Injectable()
export class MessageService {
    private _listners = new Subject<any>();

    listen(): Observable<any> {
       return this._listners.asObservable();
    }

    filter(filterBy: string) {
       this._listners.next(filterBy);
    }

}

then implement in your header component like this

// header.component.ts
@Component({
    selector: 'header',
    templateUrl: `
        <section class="container">
            <button (click)="clickFilter()">Open filter</button>
        </section>
    `
 })

export class HeaderComponent {
     @Output() onFilter: EventEmitter = new EventEmitter();

     constructor(private _messageService: MessageService){}
     clickFilter():void {
         // this.onFilter.emit('Register click');
         this._messageService.filter('Register click');
     }
 }

then use in your store component like this

@Component({
    selector: 'store',
    template: `<article class="card">
                 Test
              </article>`
})

export class StoreComponent {
    constructor(private _messageService: MessageService){
        this._messageService.listen().subscribe((m:any) => {
            console.log(m);
            this.onFilterClick(m);
        })
    }

    onFilterClick(event) {
        console.log('Fire onFilterClick: ', event);
    }
 }

The concept is that you can use observable in a service and subscribe in the component where you want it (store.component) and can send event from anywhere in the app like i did in the header.component

I hope it will help you

Sign up to request clarification or add additional context in comments.

6 Comments

Nice! Works perfectly. Also, I think this is even better to use a service because i can put all the shared functions in a single place instead of being all over the place.
little typo is there in StoreComponent (subscirbe -> subscribe), rest is working perfect for me. Thank you
What is the use of EventEmitter in header component?
see the clickFilter() in header component the line to emit event is commented so you can remove the EventEmitter from header component. i wrote it show the solution for the question
how do i unsubscribe inside store.component?
|
4

I had a similiar situation, but didn´t want to make a service for this. I managed to pass it like callback. It would be something like this:

header.component.ts

@Component({
selector: 'header',
templateUrl: `
    <section class="container">
        <button (click)="myFunction()">Open filter</button>
    </section>
`})
export class HeaderComponent {
   @Input() myFunction: Function;
}

Then just to call it passing the function and parameter

// store.html
<header [myFunction]="functionOnStore"></header>

And just declaring functionOnStore normally on store component

2 Comments

seems like this would pass a function into the component, not call it?
Yep. It will pass the function but also call/execute it. It's JS magic!!!

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.