Katana24
Katana24

Reputation: 8959

DOM not updating when data changes - Angular 5

I have a ProfileImageComponent that, when clicked, emits the user object it's associated with using my UserProfileService service. See this code:

export class ProfileImageComponent {

  @Input() user;

  constructor(private userProfileService: UserProfileService, private sidebarService: SidebarService, private storageService: StorageService, private route: Router) { }

  openUserProfile() {
    // mobile
    if(window.innerWidth < 768) {
      this.route.navigate([`/user-profile/${this.user.pk}/`]);
      this.userProfileService.openUserProfile('view', this.user);
    } else {
      // tablet and above
      let loggedInUserPk = JSON.parse(this.storageService.getItem('User'))['pk'];
      let action = this.user['pk'] === loggedInUserPk ? 'edit' : 'view';

      if(!this.sidebarService.sidebarOpened) {
        this.userProfileService.openUserProfile(action, this.user);
        this.sidebarService.openSidebar();
      } else {
        this.sidebarService.closeSidebar();
      }
    }
}

Explanation of the above:

On mobile I have a specific route url for the user profile where I route to if the window is smaller than 768px. The line after the route emits an action and the user object associated.

On tablet and above we instead open a sidebar which doesn't require a route change - it emits the value and opens the sidebar.

The UserProfileComponent that subscribes to the emitted value does receive the value regardless of screens. Here's its OnInit:

  ngOnInit() {
    this.userProfileSubscription = this.userProfileService.userProfile$
      .subscribe((profile) => {
        this.canEditProfile = profile['action'] === 'edit' ? true : false;
        this.user = profile['user'];
        this.cdr.markForCheck();
      });
  }

In my HTML I just want to display the data at this stage:

<div id="user-profile">
  <p>User : {{ user | json }}</p>
</div>

The problem: The data doesn't display on mobile unless I click twice, even though the data is definitely getting there and the model is being updated.

I tried to mark it for change detection but it didn't work (this.cdr.markForCheck();)

I also tried adding the | async pipe to the user object in the HTML but it didn't change either.

What am I doing wrong?

EDIT

The UserProfileService:

import { Injectable } from '@angular/core';
import { Subject } from 'rxjs/Subject';

@Injectable()
export class UserProfileService {

  private userProfileSource = new Subject<any>();
  userProfile$ = this.userProfileSource.asObservable();

  constructor() { }

  openUserProfile(action, user) {
    this.userProfileSource.next({ action: action, user: user });
  }

}

Upvotes: 1

Views: 1245

Answers (1)

Pierre Mallet
Pierre Mallet

Reputation: 7221

Router.navigate is asynchronous. The problem is probably that you emit your data (through this.userProfileService.openUserProfile('view', this.user) before the OnInit in UserProfileComponent.

You can use a BehaviorSubject in your userProfileService that will replay the last value emitted during the OnInit.

Upvotes: 2

Related Questions