Search code examples
angularangular-ng-if

Angular 2 hide and show element using *ngIf with boolean


I want to show and hide an element by using a button that's located in another component.

So I have a Dashboard with an amount of chambers in it and a header.

HTML of DashboardComponent with app-header and app-chamber:

 <app-header></app-header>
    <div class="container">
      <div class="row">
        <app-chamber [kamers]="kamers"></app-chamber>
      </div>
    </div>

I have this HTML wth the *ngIf in my ChamberComponent:

<div class="col-sm-6 col-md-4 col-lg-3 cardcol" *ngFor="let kamer of kamers; let i = index">
      <md-card class="chamber wit" *ngIf="kamer.patient == null">
         <p *ngIf="showId">{{kamer.id}}</p>
      </md-card>
</div>

In the HeaderComponent I have a button that needs to show and hide the element :

@Component({
  selector: 'app-header',
  templateUrl: './header.component.html',
  styleUrls: ['./header.component.css']
})
export class HeaderComponent implements OnInit {

  @Input() aList;

  dashboardComponent:DashboardComponent;

  chamberComponent:ChamberComponent;

  constructor(dashboardComponent: DashboardComponent, chamberComponent:ChamberComponent) {
    this.dashboardComponent = dashboardComponent;
    this.chamberComponent = chamberComponent;

  }

  ngOnInit() {

  }

// THIS GETS CALLED BY A BUTTON CLICK
  toggleId(){
    this.chamberComponent.toggleId();
  }

}

then my ChamberComponent code:

@Component({
  selector: 'app-chamber',
  templateUrl: './chamber.component.html',
  styleUrls: ['./chamber.component.css']
})
      export class ChamberComponent implements OnInit {

      @Input() kamers;
      showId:boolean;

      constructor() {
        this.showId=false;
      }

      ngOnInit() {

      }

      toggleId = () => {
          this.showId = !this.showId;
      }

    }

So when I click the button, the value changes (I've logged this in the console) but the view isn't updated..

When I put a button in my ChamberComponent that calls the toggleId() function the view does reveice an update and the element is shown or hidden.

But I need to toggle it from a button on my header.


Solution

  • Plunker

    There was a small mismatch between the @Input() kamers and the template *ngIf="kamer.patient == null".

    • Just change the input to kamer.

    Template HTML

    <md-card class="chamber wit" *ngIf="kamer.patient === null">
       <p *ngIf="showId">{{kamer.id}}</p>
    </md-card>
    
    <button (click)="toggleId()">Toggle</button>
    

    Component

    @Component({
      selector: 'material-app',
      templateUrl: 'app.component.html'
    })
    export class AppComponent {
    
      kamer = {
        patient: null,
        id: '1'
      };
      showId = false;
    
      ngOnInit() {
    
      }
    
      toggleId() {
        this.showId = !this.showId;
      }
    
    }
    

    Update (1) - Plunker (1)

    Use @ViewChild

    To reference a child component's functions use ViewChild

    • Place @ViewChild('child') child; in the parent component
    • In the parent template reference child functions like this: <button (click)="child.toggleId()">Toggle</button>

    Parent Component

    @Component({
      selector: 'material-app',
      template: `
        <material-child #child></material-child>
        <button (click)="child.toggleId()">Toggle</button>
      `
    })
    export class AppComponent {
    
      @ViewChild('child') child;
    
    }
    

    Child Template

    <md-card class="chamber wit" *ngIf="kamer.patient === null">
       <p *ngIf="showId">{{kamer.id}}</p>
    </md-card>
    

    Child Component

    @Component({
      selector: 'material-child',
      templateUrl: 'app.component.html'
    })
    export class ChildComponent {
    
      kamer = {
        patient: null,
        id: '1'
      };
      showId = false;
    
      ngOnInit() {
    
      }
    
      toggleId() {
        this.showId = !this.showId;
      }
    
    }
    

    Update (2) - Plunker (2)

    Use @Output() + EventEmitter

    To extend the previous setup to use a sibling component you can

    • Have the sibling emit an event using an EventEmitter
    • Listen to the emitted event in the parent component, and call the child function needed using ViewChild

    Sibling Component

    @Component({
      selector: 'material-sibling',
      template: `
        <button (click)="toggle.emit()">Toggle</button>
      `
    })
    export class SiblingComponent {
      @Output() toggle = new EventEmitter();
    }
    

    Parent Component

    @Component({
      selector: 'material-app',
      template: `
        <material-child #child></material-child>
        <material-sibling (toggle)="child.toggleId()"></material-sibling>
      `
    })
    export class AppComponent {
    
      @ViewChild('child') child;
    
    }
    

    Child Template

    <md-card class="chamber wit" *ngIf="kamer.patient === null">
       <p *ngIf="showId">{{kamer.id}}</p>
    </md-card>
    

    Child Component

    @Component({
      selector: 'material-child',
      templateUrl: 'app.component.html'
    })
    export class ChildComponent {
    
      kamer = {
        patient: null,
        id: '1'
      };
      showId = false;
    
      ngOnInit() {
    
      }
    
      toggleId() {
        this.showId = !this.showId;
      }
    
    }