0

I'm trying to get a click event on one sibling component to be 'heard' by another sibling component.

Should I pass the event up the chain to parent, detect the change and then pass it to the sibling (a different child to a parent) via parent somehow?

Components in the page template:

<wrap>
  <toggler>Toggle the Box</toggler>
  <box>Box Content ...</box>
</wrap>

Toggler Component:

@Component({
  selector: 'toggler',
  template: `<div (click)="toggleBox()"><ng-content></ng-content></div>`,
})
export class TogglerComponent {
  @Output() toggle = new EventEmitter<boolean>();
  visible: boolean = false;
  toggleBox(): void {
    this.visible = !this.visible;
    this.toggle.emit(this.visible);
  }
}

Box Component: How can the box component pick up toggle event?

Basic component:

@Component({
  selector: 'box',
  template: `<ng-content *ngIf="toggle"></ng-content>`,
})
export class BoxComponent {
  @Input() toggle: boolean;
}
GRowing
  • 4,629
  • 13
  • 52
  • 75
  • 1
    https://angular.io/guide/component-interaction – jonrsharpe May 22 '18 at 20:30
  • `@Output` decorator provides a mechanism for a child component to emit events up to its parent component. `emit()` method pushes the event up to the parent component. It's not for cross-component communication You should use service and [RXJS](https://stackoverflow.com/a/49388249/5695162) to communicate between sibling components – Vikas May 22 '18 at 20:33
  • @jonrsharpe I am aware of angular documentation. You're not helpful. Do you have any specific ideas on how to address communication between sibling components? – GRowing May 22 '18 at 20:37

1 Answers1

1

There are several ways to accomplish sibling component communication:

1. Service

The first option is to have a service with a public observable property and inject it into the components.

@Injectable()
export class ToggleService {

  private toggle = new Subject<boolean>();

  public $toggle = this.toggle.asObservable();

  constructor() { }

  setToggle(val: boolean){
    this.toggle.next(val);
  }

}

From there your components would be

TogglerComponent
export class TogglerComponent {


  constructor(toggleService: ToggleService){}

  visible: boolean = false;
  toggleBox(): void {
    this.visible = !this.visible;
    this.toggleService.setToggle(this.visible);
  }
}
BoxComponent
export class BoxComponent implements OnInit {

   constructor(toggleService: TogglerService){

   }

   ngOnInit(){
     this.toggleService.$toggle.subscribe(val => {
       // Do Something
     });
   }
}

Note: With this approach you need to be careful with the DI scope, as it is necessary that both components share the same instance of the service. So your provider should be either in either the parent component or a shared or parent module.

2. Parent Property

The second alternative is to have a property in the parent and bind it to both components.

Your ParentComponent would be something ilke

export class ParentComponent implements OnInit {

  public toggle: boolean;

  constructor() { }

  ngOnInit() {
  }

  changeToggle(val){
    this.toggle = val;
  }

}

And your template would just bind that function and property

<toggler (toggle)="changeToggle(toggle)"/>
<box [toggle]="toggle"/>

3. BindingLess Solution (Not Recommended)

Based on your comment there is another alternative, using ViewChild, which would look something like:

export class ParentComponent implements AfterViewInit {

  @ViewChild(TogglerComponent)
  togglerComponent: TogglerComponent;

  @ViewChild(BoxComponent)
  boxComponent: BoxComponent

  constructor() { }

  ngAfterViewInit() {

    this.togglerComponent.toggle
      .subscribe(val => this.boxComponent.toggle = val)

  }

}

For further reference you could check: https://angular.io/guide/component-interaction

J. Pichardo
  • 3,077
  • 21
  • 37
  • 1
    Thank you! These are helpful suggestions :). I am trying, for the moment, to use the event emitter,, detect a change in the parent component and pass it to a target child but I want to keep the component selectors free from binding anything to them. The service method looks promising. – GRowing May 22 '18 at 20:48
  • I do not find any use for the `Output` decorator without binding, if you really want to keep you selectors free from binding then `ViewChild` should do the trick – J. Pichardo May 22 '18 at 20:54
  • Why would use of ViewChild not be recommended? – GRowing May 23 '18 at 01:34
  • Oh I see. Thank you. – GRowing May 23 '18 at 01:36