Ionic 4: View not updating after recieving push notification

When I recieve a Push notification I add something to an array. I use that array in my html to show messages like on whatsapp. All works fine when I use buttons and I checked my Code for more than 12h now and it should normally work right. The problem is that when I recieve a push notification the view doesn’t refresh even if the console logs are right and the array gets extended. When I use a button to add something to the array all works fine. I can share my code if needed but I think that isn’t even necessary for a solution (push notifications itself are working).

I think it is, especially the specific push notification plugin and associated wrapper that you’re using. What you describe sounds suspiciously like accidentally jumping out of Angular’s UI zone. If you are receiving a Promise from an external source (like a plugin), try wrapping it in what would ordinarily seem to be a spurious second Promise, like so:

before

myPlugin.notificationReceived().then(n => doStuff());

after

Promise.resolve(myPlugin.notificationReceived()).then(n => doStuff());

Ok, my fcm.service and the function we are talking about is this, the part after refresh chat:

onNotifications() {
    this.firebase.onMessageReceived().pipe().subscribe((res => {
      console.log("this notification", res)
      if (this.authService.authenticationState.value) {
        console.log("trigger functions get messages")
        console.log("Tapped in: ", res.tap)
        this.inboxService.getMessages()

        //refresh chat   
        if (res.alert == "NEW_CHAT_MESSAGE") {
          if (res.tap != undefined) {
            console.log("Navigate to Chat ", res.chatid, " after push, halterid: ", res.halterid)
            this.chatService.newORoldChat(res.halterid).then(res_new_old => {
              console.log("new or old", res_new_old)
              let navigationExtras: NavigationExtras = {
                state: {
                  index: res_new_old
                }
              }
              this.navController.navigateForward("chatsingle", navigationExtras)
              this.chatService.enterdChat(res.halterid)

            })

          } else {
            //only refresh
            console.log("Only Refresh chat ", res.halterid)
            this.chatService.refreshUnreadCount()
            //need to check if on right page
            this.chatService.all_stored_chats.value[0].nachrichten.push({
              "id": 340,
              "sender": 5066,
              "text": "I'm a test message",
              "filename": "",
              "gesendet": "Today 15:44",
              "gelesen": ""
          })
            if (res.halterid == this.chatService.chat_partner.value.id){
              this.activeChat.next(true)
            } else {
              console.log("refreshed on service")
              this.chatService.getChat(res.halterid)
            }
            
            

          }
        }

        //navigate to inbox
        //navigate to tippspiel
        //navigate to profile->orders
      }
    }))
    return this.firebase.onMessageReceived();
  }

On the chat.page.ts I subscribed to the all_stored_chats BehaviorSubject

Aside from the potential problems with zones previously mentioned, I would recommend never poking around in the internals of a BehaviorSubject like this. In fact, I never publicly expose a BehaviorSubject from a service, preferring instead to do something like this:

private underlyingSubject = new BehaviorSubject<string>("foo");
public usableObservable: Observable<string> { return this.underlyingSubject }

The theoretical point of this is encapsulation, so that you can change the implementation later without worrying about impact on consumers. The practical point is that subscriptions won’t fire when you directly poke around inside the BehaviorSubject - you need to call next() on it in order for that to happen.

Yeah you are totally right, that’s my bad. I only wanted to use that as a debug case, normally I’m only changing the activeChat with next and then I call an http request to update the whole all_stored_chats Subject. I only wanted to get on the lowest level of the problem without changing too much code. So now about the zones, did I understood you right, that I just need to pack the whole function into a promise?

That’s only if you’re receiving a Promise in the first place. In this situation, you’ve got an Observable. Let’s see…you’ve got that odd unused pipe there…maybe we could McGyver something out of that.

this.firebase.onMessageReceived()
  .pipe(msg => of(msg))
  .subscribe(...)

That should achieve the same effect of warping you into the right zone if you’re not already.

Now that I’ve seen the code, though, I’m leaning towards the fact that you’re never calling next as a more likely explanation for the issue.

Ok, I built that in, thanks already. I will know if that changed something in about 8mins.

About calling next:
On the actual chat page, I got this, as part of the constructor:

constructor(
    private chatService: ChatService,
    private photoViewer: PhotoViewer,
    private photoService: PhotoService,
    private alertController: AlertController,
    private file: File,
    private route: ActivatedRoute,
    private router: Router,
    private fcmService: FcmService
  ) {
    this.getData()
    this.fcmService.activeChat.subscribe((value) =>{
      console.log("activeChat", value)
      if (value){
        console.log("Trigger test function")
        this.reloadforMessages()
        this.fcmService.activeChat.next(false)
      }

And this is the reloadforMessages function, that should be called when recieving a notification:

reloadforMessages(){
    console.log("Reaload for new Message")
    var DateNow = new Date
    var writing_obj_display = {
      sender: this.chatService.chat_partner.value.id,
      text: "...",
      filename: "",
      gesendet: "Heute " + moment(DateNow).format("HH:mm"),
      gelesen: "1",
      writing: ""
    }
    this.my_active_chat["nachrichten"].push(writing_obj_display)
    this.chatService.getChat(this.chatService.chat_partner.value.id).then(_ => {
      console.log(this.my_active_chat)
      console.log(this.chatService.all_stored_chats.value[this.chat_index])

      this.my_active_chat["nachrichten"] = this.chatService.all_stored_chats.value[this.chat_index].nachrichten
      this.content.scrollToBottom()
    })
  }

The short part for test cases was or better is that:

test(){
    console.log("refreshed on single chat")
    this.my_active_chat["nachrichten"].push({
      "id": 340,
      "sender": 5066,
      "text": "Ich bin ein TestButton",
      "filename": "",
      "gesendet": "Heute 15:44",
      "gelesen": ""
  })
    console.log(this.my_active_chat)
    console.log(this.chatService.all_stored_chats.value[this.chat_index])
    this.my_active_chat = this.chatService.all_stored_chats.value[this.chat_index]
    console.log(this.my_active_chat)
    this.content.scrollToBottom()
  }
s$ = new BehaviorSubject<string[]>([]);
s$.subscribe(ar => { doStuff() });
s$.value[0] = "foo"; // will *not* fire the above subscription
{
  let sv = s$.value;
  sv[0] = "foo";
  s$.next(sv);  // *will* fire the above subscription
}

I added this, and let all other code like it was in the subscibe, but now nothing is happening at all, I don’t even get the console log, that there was a notification.

this.firebase.onMessageReceived()
.pipe(msg => of(msg))
.subscribe(
(res => {
      console.log("this notification", res)
}))

Sorry, my bad, doing three things at once, all poorly.

pipe(switchMap(msg => of(msg))

Thanks, good to know, but I use next in the in this if:

if (res.halterid == this.chatService.chat_partner.value.id){
              this.activeChat.next(true)

The all_stored_chats Subject could also be a normal variable at that point, because I think I removed the part where it was necessary to subscribe to it, and no problem, don’t stress yourself :slight_smile:

Ok that worked, I’m getting my console logs again, the test() function gets called, I see the console log and the dummy object got pushed into the my_active_chat["nachrichten"] array. But I still didn’t see this dummy message in the view.

To recap a bit, you originally said:

So we can split the problem in half:

A. Getting my_active_chat["nachrichten"] updated
B. Reflecting A in the view

At the beginning of this conversation, I was under the impression that the problem was squarely in part A, because I took your “when I use a button everything is fine” to indicate that the button took care of A, and in that event, B worked as expected.

Now I’m hearing that A has been eliminated as a source of trouble, which would seem to also let push notifications off the hook.

What is different between the current state of affairs and the “manually pushing something into my_active_chat["nachricten"] via a button” one?

Yeah sorry, it’s late in germany… It was all the time about B.
I really have to say that I got no Idea what the difference is.

From the what is done perspective:
C. The push notification changes a Subject, that change calls a function that pushes someting into an array.
D. The push on a button only calls the same function where something is pushed into the array.

In the console both cases get executed, but only D is visible, because another textbox is created in the html page.

Maybe the button works, because I interact with the site directly and the push notification “only” calls functions in the site background?

One further quick thing you can check:

  • Inject a ChangeDetectorRef into your page constructor.
  • At some point after the array has been modified, call its detectChanges method.

If that works, then Angular’s change detection is failing to pick up your change for some reason. This used to be a much more common and insidious problem back in the AngularJS days, but I guess it’s conceivable that it could still bite one under some conditions.

Thanks that was the solution, all works now :smiley:
Or atleast the test case, I will check if works with the http request too and reply again then.

It works with the real version too, thanks a lot :slight_smile:

In the long term, it’s probably best to try to figure out exactly why change detection isn’t triggering, but at least this should band-aid it for now.