2
\$\begingroup\$

I have a tabs nav element that has two tabs, it needs to show a component based on what tab was clicked and hide the other component. If the clicked tab is already "active", the component needs to remain showing.

I have this working, but it seems very inefficient to me. Can anyone show me a better way to do this?

Here's how I have it set up now. For the sake of not posting every file in the question, know that the project is set up correctly.

@Component({
  selector: 'my-app',
  template: `
    <div>
      <button type="button" (click)="changeShowStatus(oneShowing=true,twoShowing=false)">1</button>
      <button type="button" (click)="changeShowStatus(twoShowing=true,oneShowing=false)">2</button>
      <div class="box1" *ngIf="oneShowing">
        <p>some content</p>
      </div>
      <div class="box2" *ngIf="twoShowing">
        <p>some content2</p>
      </div>
    </div>
  `,
})
export class App {
  name:string;
  oneShowing:boolean;
  twoShowing:boolean

  constructor() {
    this.oneShowing = true;
    this.twoShowing = false
  }
}

Plunker

\$\endgroup\$
  • \$\begingroup\$ What exactly do you mean by "inefficient" \$\endgroup\$ – Igor Soloydenko Oct 20 '17 at 17:47
  • \$\begingroup\$ @IgorSoloydenko I mean, there has to be a better way of doing this, right? \$\endgroup\$ – Robert Oct 20 '17 at 18:22
1
\$\begingroup\$

Separate content and logic

It is a good practice (recommended by Angular style guidelines) to separate component template and component code.

Move template to an own my-app.component.html file and refer to it via templateUrl property instead of template.

Avoid logic in your template

oneShowing=true,twoShowing=false in and twoShowing=true,oneShowing=false parts are technically logic. It does need to be in the template, and it should not be there.

my-app.component.html

<button type="button" (click)="displayBoxWithNumber(1)">1</button>
<button type="button" (click)="displayBoxWithNumber(2)">2</button>

my-app.component.ts

displayBoxWithNumber(boxToShow: number): void {
  this.hideAllBoxes();
  this.displayBox(boxToShow);
}
private hideAllBoxes(): void {
  this.oneShowing = false;
  this.twoShowing = false;
}
private displayBox(boxToShow: number): void {
  if (boxToShow === 1)
    this.oneShowing = true;
  else if (boxToShow === 2)
    this.twoShowing = true;
  else
    throw new Error(`Invalid box number provided ${boxToShow}`);
}

Please notice that the code above is for illustrational purposes in your specific context. Any more or less real project will use *ngFor directive to iterate over some data source to construct the boxes dynamically. Something like the code below. Do not worry if you don't understand it yet. You'll get there later with a bit of practice.

<div *ngFor="let box of boxeList; boxIndex = index; trackBy: trackByBoxId">
  <button type="button" (click)="displayBox(box, boxIndex)"> {{ box.number }} </button>
  <div class="box" *ngIf="isBoxActive(box)">
    <simple-box-view [value]="box"></simple-box-view>
  </div>
</div>

Know your tool/language

In typeScript you can initialize class' properties inline (this is the same as setting the values in constructor). You can also omit the type of the variable it is inferred. Similarly, if your constructor does not do any work, it can be got rid of.

export class App {
  name: string;
  oneShowing = true;
  twoShowing = false;

  // Methods...
}
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.