Angular2 dart ngFor not updating view when using WebSockets

Issue

I’ve been having a problem with an angular2 component. The view doesn’t update when I’m using websockets. I’ve tried with http requests and it works fine, but I need to keep the data in the view updated.

I can connect to the server just fine using websockets and I can recieve the data, but my view doesn’t update.

@Component(
  selector: "pv-table",
  templateUrl: "./table.component.html"
)

class TableComponent
{
  WebSocket _connection;

  // NgZone _zone;
  // ApplicationRef _application_ref;

  List data;

  TableComponent(/* this._zone, this._application_ref */)
  {
    _connection = new WebSocket( "ws://${HOST}:${PORT}" );

    _connection.onOpen.first.then( ( _ ) {
      _connection.onMessage.listen( ( MessageEvent e ) => OnMessage( e.data ) );

      // A simple class that gives my application a standard
      // way to communicate.
      Packet request = new Packet()
        ..message = "table.get"
        ..data    = {};

      _connection.send( JSON.encode( request ) );

      _connection.onClose.first.then( ( _ ) { });
    });
  }

  void OnMessage( String data )
  {
    Map    res = JSON.decode( data );
    String cmd = res[ "message" ];

    Log.Info( cmd );

    switch( cmd )
    {
      case "table.send":
        // Setting the data here. This works
        data = res[ "data" ];

        // This prints the correct data in the developer console.
        Log.Info( data ); 

        // Neither of these help.
        // _zone.run( () => data = res[ "data" ] );
        // or
        // _application_ref.tick();

        break;

      default:
        Log.Warn( "Unknown command: $cmd" );
        break;
    }
  }
}

I’ve googled around and seen some problems like this where forcing change decection with NgZone.run(...) or ApplicationRef.tick(), but that didn’t help. Either they don’t work for this situation or I don’t know how to use them.

My template looks like this:

<p *ngFor="let person of data">
  <span scope="row">{{ person[ "_id" ] }}</span>
  <span>{{ person[ "name" ] }}</span>
  <span>{{ person[ "job" ] }}</span>
  <span>{{ person[ "date_due" ] }}</span>
</p>

As a comment mentions. I can see the data being printed in the developer console. It is in the correct format, a list of maps with the correct fields, but the page remains blank.

Solution

Turns out I was just being really, really stupid.

Lets just take a look at a few things shall we?

The signature for the method that handles incoming data:
void OnMessage( String data )

The member that I am trying to use in the template:
List data;

The assignment inside of the OnMessage method:
data = res[ "data" ];

Notice anything strange?
Maybe that the class member and the method parameter have the same name?
Maybe that means the parameter is shadowing the member?
Maybe that means an assignment to that name is actually to the parameter and not the member?

The worst part is I’ve been sat here for almost two hours trying to figure out what the problem was.

Changing two lines and everything works

  • void OnMessage( String data ) => void OnMessage( String response_data )
  • Map res = JSON.decode( data ); => Map res = JSON.decode( response_data );

Answered By – Hector

Answer Checked By – Katrina (FlutterFixes Volunteer)

Leave a Reply

Your email address will not be published. Required fields are marked *