0

I am having 2 components that do not have any parent-child relationship.

One component is a simple login.component.html:

<form #loginForm="ngForm" (ngSubmit)="onSubmit()">
  <div class="login-container">
    <mat-form-field appearance="fill">
      <mat-label>Enter your username</mat-label>
      <input
        matInput
        name="username"
        type="text"
        [(ngModel)]="username"
        #usernameCtrl="ngModel"
        required
      />
    </mat-form-field>
    <mat-form-field appearance="fill">
      <mat-label>Enter your password</mat-label>
      <input
        matInput
        name="password"
        [type]="hide ? 'password' : 'text'"
        [(ngModel)]="password"
        #passwordCtrl="ngModel"
        required
      />
      <button
        mat-icon-button
        matSuffix
        (click)="hide = !hide"
        [attr.aria-label]="'Hide password'"
        [attr.aria-pressed]="hide"
      >
        <mat-icon>{{ hide ? "visibility_off" : "visibility" }}</mat-icon>
      </button>
    </mat-form-field>
  </div>
  <div>
    <button
      mat-button
      color="primary"
      [disabled]="!loginForm.form.valid"
      type="submit"
    >
      Login
    </button>
  </div>
</form>

and the other is a menu-bar.component.html:

<div>
    <h1>Hello world!</h1>
    <h2>I am visible in world all of the time!</h2>
    <div *ngIf="loginSuccessfull"> <menu-app> </menu-app> </div>
</div>

When the user logs in successfully, I want to show my menu-app.component. What I did is that I defined a boolean variable that set it true when user logs in.

In login.component.ts:

onSubmit() {
    if (this.username === 'a' && this.password === 'a') {
      console.log('login successful');
      this.loginService.updateLoginSuccessfull(true);
    }

In menu-bar.component.ts:

ngOnInit() {
    this.loginService.loginSuccessfull.subscribe(loginSuccessfull => this.loginSuccessfull = loginSuccessfull);
  }

  ngAfterContentChecked() {
    this.loginService.loginSuccessfull.subscribe(loginSuccessfull => this.loginSuccessfull = loginSuccessfull);
  }

As you can understand, both components are calling a service which is defined login.service.ts:

export class LoginService {

  private onLogin = false;
  private onLoginSuccessfull = new BehaviorSubject(this.onLogin);
  loginSuccessfull = this.onLoginSuccessfull.asObservable();

  constructor() { }

  updateLoginSuccessfull(loginSuccessfull: boolean) {
    this.onLoginSuccessfull.next(loginSuccessfull);
  }

}

What am I missing? Is there any other way to do this change detection dynamically? Thanks! The expected behaviour is to show the <menu-app></menu-app> only when the user logs in. If the user does not log in, he should not see the component. The way I implemented does not show the component even though the use logs in successfully.

Kathrine Hanson
  • 575
  • 2
  • 10
  • 32
  • 1
    Can you describe the behavior you're getting and how that differs from what you expect? – Chris Danna Jun 04 '20 at 14:52
  • @Chris of course. I forgot to write it. I am sorry. – Kathrine Hanson Jun 04 '20 at 18:19
  • Did you specify `ChangeDetectionStrategy` on the menu-bar component? – Chris Danna Jun 04 '20 at 18:28
  • @Chris hi! yes! I just tried ur solution but unfortunately it does not work. – Kathrine Hanson Jun 04 '20 at 18:30
  • @Chris is it a problem that the menu-bar.component.html is already rendered and then we change it? it should have worked with ur solution :/ – Kathrine Hanson Jun 04 '20 at 18:31
  • If you specify OnPush, then subscribing to the observable and setting the local field will not work, you will need to use Default detection strategy, or refactor to use the async pipe. It shouldn't matter that menu-bar is already rendered. Perhaps you can recreate the behavior in a stack blitz and I can look at that? – Chris Danna Jun 04 '20 at 18:33
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/215331/discussion-between-chris-and-kathrine-hanson). – Chris Danna Jun 04 '20 at 18:55

1 Answers1

0

The components look mostly fine. Are you using OnPush change detection for the menu-bar component? You are also creating 2 subscriptions in the menu-bar component in ngOnInit and ngAfterContentChecked. This will update the loginSuccessful field twice each time a value is emitted from the service. Neither of these are needed if you use the async pipe. The async pipe automatically subscribes (and unsubscribes) to the Observable, will trigger change detection when using OnPush, and reduces the code needed in the component.

menu-bar.component.ts

@Component({
  selector: 'menu-bar',
  templateUrl: './menu-bar.component.html',
  styleUrls: ['./menu-bar.component.css'],
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class MenuBarComponent {
  loggedIn$ = this.service.loginSuccessful;

  constructor(private service: LoginService) { }
}
<div>
  <h1>Hello world!</h1>
  <h2>I am visible in world all of the time!</h2>
  <div *ngIf="loggedIn$ | async"> <menu-app> </menu-app> </div>
</div>

https://stackblitz.com/edit/angular-ivy-pzss5d

Chris Danna
  • 1,164
  • 8
  • 17