View not updating after push notification

I need to show push notifications messages on a message list in a view currently opened. This messages must be shown automatically and in realtime meaning if I receive a push notification it is immediately shown in the view whitout user manual refresh.

I have a message view and a message provider.

The provider keeps listening on push notification events. When the provider receives an notification it call a callback function, which is an method of the message view, passing the new message so it can be shown in this view. The message is put in a array inside the view which is binded to the layout of the view.

The problem is that the new messages, received by the callback are not shown in the view.

I know the callback is beind called because I put a log in this callback. This logs prints the message and it is there. The binding is also correct because I load messages saved on database before that array and they are shown in the view and also because if I put an message created on the view it self, not received from push, in the array it is rendered in the view.

There is also an input to send messages. If I click in this input and close the keyboard the view is updated and the new messages shown.

My question is why are not my push messages beinge rendered and how could I make they be rendered?

The problem is not just with the array. Any variable modified via a callback comming from a push notification is not updated in the view.

Is there some way to force the view to update?

I would modify the provider to expose an Observable instead of calling callbacks. Your code is probably running in the wrong zone. Yes, you could call zone.run() explicitly. I think this is a perfect use case for Observable, though, and that should automatically be zone-aware.

rapropos, still not getting the view uptaded.

This is my code:

push.html

<ion-header>

  <ion-navbar primary>
    <ion-title>push</ion-title>
  </ion-navbar>

</ion-header>


<ion-content padding>
  <ion-list>
    <ion-item *ngFor="let message of messages">
      <h1>{{message.subject}}</h1>
      <p>{{message.content}}</p>
    </ion-item>
  </ion-list>
</ion-content>

push.ts

    import { Component } from '@angular/core';
    import { NavController, Platform } from 'ionic-angular';
    import { PushProvider } from '../../providers/push-provider/push-provider'

    @Component({
      templateUrl: 'build/pages/push/push.html',
      providers:[PushProvider]
    })
    export class PushPage {

      messages = [
        {
          subject: 'Subject 3',
          content: 'Content 3'
        },{
          subject: 'Subject 2',
          content: 'Content 2'
        },{
          subject: 'Subject 1',
          content: 'Content 1'
        },

      ]

      constructor(private nav: NavController, private platform: Platform, private pushProvider: PushProvider) {
    	  var thisObj = this;
        this.platform.ready().then(() => {
          this.pushProvider.init();
          this.pushProvider.messages.subscribe(data=>{
            thisObj.messages.unshift({
              subject: data.title,
              content: data.message
            });
          })      
        });
      }
    }

push-provider.ts

export class PushProvider {
  
  messages: any;
	
  init(){
  	var push = Push.init({
        android: {
          senderID: "<MY_SENDER_ID>"
        },
        ios: {
          alert: "true",
          badge: true,
          sound: 'false'
        },
        windows: {}
      });
      push.on('registration', (data) => {
        console.log('REGISTRATION ID', data.registrationId);
      });
      this.messages = new Observable(observer=>{        
        push.on('notification', (data) => {              
          observer.next(data);
        });
      });

      push.on('error', (e) => {
        console.log(e.message);
      });
  } 
}

Hmm. This should really be rolled into the ionic-native provider itself, but until then, can you try injecting NgZone in your push-provider’s constructor and wrapping the call to your Observable’s next in zone.run()? You could also / additionally try using BehaviorSubject in order to catch old notifications.

1 Like

Solved!

In my original code I was saving the notification in the database, which return a promise, of course.Then, in case of success, I notify the view class. The view class then reads the database to retrieve the data it shows.

The problem: It’s not synchronized. The data loaded from database is not the newest data.

Solution: as a workaround I added a delay on load data from database using setTimeout function.

I would not be satisfied with using setTimeout, but it’s not my problem, I guess.

1 Like

I am not completely satified. As I said it’s a workaroud. I still will try your last sugestion.

Anyway. This is a case of a promise not reallly fulfilled. I will analyze the case in more details, but it may affect other users.

@Natanael4354 Did you solve your issue? Can you share your solution perhaps??

I receive notifcation in App, Alert get shown, but the array of notifications is not updated, unless I touch the view.

thebasix, I don’t remember. But I was doing it the wrong way.

The correct way is to use a shared service. A shared service is a provider which is declareted only in the app.component’s providers array and injected in the constructors of each pages or components that will use it. By declaring the shared service only in the app.component providers array, this provider will have a unique instance of the service shared among pages or components where it is injected.

Then you import a subject inside the shared service:

import { Subject } from 'rxjs/Subject';

And declare a subject as attribute of your SharedService class:

class SharedService {
       mySubject:Subject<any> = new Subject();
}

A subject allows to send and receive messages.

You can subscribe it in one module this way:

constructor(privete mySharedService: SharedService) {
      this.mySharedService.mySubject.subscribe (
            function (parameters) { console.log(parameters) }, 
            function (error) { console.error(error) } 
      );
}

This way you is always notified when other module sends a message:

constructor(private mySharedService: SharedService) {
      this.mySharedService.mySubject.next({ parameter: 'some value'})
}

Finally, you can create a shared service which receives push notications and adds this notifications to an array or send this nofications via subject. This array then can be linked to the template via data binding. Or the view can subscribe the service of the shared service and receive the message. This way view will always show, in a reactive way, the data received.

@Natanael4354 I’m not sure if you understood my issue. I’ve created a thread for my issue and I would appreciate it if you could just read the first post and watch the short video I attached where my issue is demonstrated. Then you could tell me if you answer above would solve my issue (I dont think it would, that’s why I post this).

Thanks

your solution really helped me

I agree with you, it’s very clear!