Toggle property in parent Component from Child Com

2020-04-07 07:01发布

问题:

I'm currently teaching myself Angular2 and I have a practical problem that would be easy to rectify in AngularJS but currently I'm looking through examples to find a solution with Angular2. Anyway, I have a top level component called App and within that component I have a property that applies a css class, here is my HTML that is used as the component template - you can see where I wish to apply the css class using ngClass. The showMenu value is just a boolean.

<div class="navigation" role="navigation">
    <ul>
        <li><a [routerLink]="['Home]">Home</a></li>
        <li><a [routerLink]="['Mail']">Mail</a></li>
        <li><a [routerLink]="['Friends']">Friends</a></li>
        <li><a [routerLink]="['Games']">Games</a></li>
    </ul>
</div>
<!-- below is where I wish to toggle the class -->
<div class="site-wrap" [ngClass]="{'slide-right': showMenu}">
    <div class="container-fluid">
        <div class="row nav">
            <top-navigation></top-navigation>
        </div>
    </div>

    <div class="container-fluid content-gradient">
        <div class="row">
            <div class="col-md-10">
                <router-outlet></router-outlet>
            </div>
            <div class="col-md-2">
                <contacts-list></contacts-list>
            </div>
        </div>
    </div>

    <div class="container-fluid footer">
        <footer-navigation></footer-navigation>
    </div>
</div>

Here is the Component Code for App (not much to see here...)

export class App {

    showMenu: boolean = false;
    constructor() {
    }
    // I need to listen for the toggle somehow?
    toggleMenu():void {
        this.showMenu = !this.showMenu;
    }

}

Now you can see I have a child component with the selector top-navigation. This component TopNavigation has a method on a button that I wish to toggle the value of showMenu in the parent / top-level component. Here's my code so far (just small extracts)

export class TopNavigation {

          constructor() {
            // Things happening here
          }

          toggleMenu():void {
            // I want to toggle the parent property by somekind of $emit
          }
    }

here is the template HTML (not all) for the Component.

<div class="col-xs-2 col-sm-1  hidden-md hidden-lg hamburger-menu">
    <button class="btn btn-default" (click)="toggleMenu()">&#9776;</button>
</div>

In AngularJS I would have an $scope.$emit on the toggleMenu function and in the parent I would have a $scope.$on to listen for the event. I'm currently reading about EventEmitters in Angular2 however if anyone has a quick explanation/example I would be most appreciative especially as I think this is a common / practical everyday task for Angular developers.

回答1:

I could leverage a shared service that define an EventEmitter property. Your App component will subscribe on it to be notified when the corresponding event is trigger by the TopNavigation component.

  • Shared service

    @Injectable()
    export class MenuService {
      showMenuEvent: EventEmitter = new EventEmitter();
    }
    

    Don't forget to define the corresponding provider in the boostrap function to be able to share the same instance of the service for the whole application: `bootstrap(App, [ MenuService ]);

  • App component

    @Component({ ... })
    export class App {
      showMenu: boolean = false;
      constructor(menuService: MenuService) {
        menuService.showMenuEvent.subscribe(
          (showMenu) => {
            this.showMenu = !this.showMenu;
          }
       );
     }
    

    }

  • TopNavigation component:

    export class TopNavigation {
      constructor(private menuService: MenuService) {
        // Things happening here
      }
    
      toggleMenu():void {
        this.showMenu = this.showMenu;
        this.menuService.showMenuEvent.emit(this.showMenu);
      }
    }
    

See this question for more details:

  • Delegation: EventEmitter or Observable in Angular2


回答2:

You can use two-way binding.

<top-navigation [(toggleMenu)]="showMenu"></top-navigation>

maybe one-way would do as well, I don't know your exact requirements

<top-navigation (toggleMenuChange)="showMenu=$event"></top-navigation>
export class TopNavigation {
      @Output() toggleMenuChange: EventEmitter = new EventEmitter();

      // for two-way binding
      @Input() toggleMenu:boolean = false;

      constructor() {
        // Things happening here
      }

      toggleMenu():void {
        this.toggleMenu = !this.toggleMenu;
        this.toggleMenu.emit(this.toggleMenu);
      }
}