toggle sidenav from another component in angular

657 views Asked by At

I want to toggle a sidenav from another component in Angular. I have 3 child Components: menu,content,header. Button is on header component.I want to click on button then menu should toggle.

header.ts

export class HeaderComponent implements OnInit {
  constructor(public service: SharedService) {}

  ngOnInit() {}

  toggleMenu() {
    this.service.flag = !this.service.flag;
    this.service.flagChange.emit(this.service.flag);
  }
}

header.html

<div>
  <a href="">
    <i class="fas fa-bars text-white barsIcon" ` (click)="toggleMenu()"></i>
  </a>
</div>

menu.ts

export class MenuComponent implements OnInit {
  constructor(public service: SharedService) {
    this.service.flagChange.subscribe((res) => this.service.flag);
  }

  ngOnInit() {}
}

menu.html

<div>
  <ul>
    <li>
      <a routerLink="/dashboard">Dashboard</a>
    </li>
    <li>
      <a routerLink="/user">User</a>
    </li>

    <li>
      <a routerLink="">Currency</a>
    </li>
    <li>
      <a routerLink="/report">Report</a>
      <ul>
        <li>
          <a routerLink="">Report1</a>
        </li>
        <li>
          <a routerLink="">Report2</a>
        </li>
        <li>
          <a routerLink="">Report3</a>
        </li>
      </ul>
    </li>
  </ul>
</div>

service.ts

@Injectable({
  providedIn: "root",
})
export class SharedService {
  public flag: boolean = false;
  public flagChange = new EventEmitter<boolean>();

  constructor() {}
}
1

There are 1 answers

5
Raphaël Balet On BEST ANSWER

You aren't that fare, but you should use BehaviorSubject and not EventEmitter for that purpose
Emitter a meant for @Output()

here some little changes.
I also refactor a little bit your code ;)

header.ts

export class HeaderComponent {
  constructor(public sharedService: SharedService) {}
}

header.html

<div>
  <a href="">
    <i class="fas fa-bars text-white barsIcon" (click)="sharedService.toggleMenu()"></i>
  </a>
</div>

menu.ts

export class MenuComponent implements OnInit, OnDestroy {
  protected _unsubscribe$: Subject<void> = new Subject();

  flag?: boolean

  constructor(private _sharedService: SharedService) {}

  ngOnInit() {
    this._sharedService.onFlagChange$
      .pipe(takeUntil(this._unsubscribe$))
      .subscribe((value) => {
        this.flag = value
      })
  }

  ngOnDestroy(): void {
    this._unsubscribe$.next();
    this._unsubscribe$.complete();
  }
}

menu.html

<div [class.hidden]="!flag"> <!-- or *ngIf, as you wish ;) -->
  <ul>
    <li>
      <a routerLink="/dashboard">Dashboard</a>
    </li>
    <li>
      <a routerLink="/user">User</a>
    </li>

    <li>
      <a routerLink="">Currency</a>
    </li>
    <li>
      <a routerLink="/report">Report</a>
      <ul>
        <li>
          <a routerLink="">Report1</a>
        </li>
        <li>
          <a routerLink="">Report2</a>
        </li>
        <li>
          <a routerLink="">Report3</a>
        </li>
      </ul>
    </li>
  </ul>
</div>

service.ts

@Injectable({
  providedIn: "root",
})
export class SharedService {
  flag = false;
  onFlagChange$: BehaviorSubject<boolean> = new BehaviorSubject(this.flag);

  constructor() {}

  toggleMenu() {
    this.flag = !this.flag;
    this.onFlagChange$.next(this.flag); // Here, .next instead of .emit
  }
}

Error

  1. Using BehaviorSubject instead of EventEmitter

Optimization

  1. Always unsubscribe from observable!

Refactoring

  1. Do not declare :boolean if you're already adding a value to it, this is implicit.
  2. Move toggle logic inside the service
  3. Do not declare ngOnInit if not used
  4. Make services private if not used in the html file