2
votes

I have an angular 5 app with an ngrx store for my widgets which are stored with an backend (socket.io). This is how my widget looks.

widgets: {
      1: {
        id: 1,
        type: 'datatable',
        startDate: startDate,
        endDate: endDate,
        dataStream: ''
      },
      2: {
        id: 2,
        type: 'chart',
        startDate: startDate,
        endDate: endDate,
        dataStream: ''
      }
    }

Every Widget has a startdate and an enddate. I have already some effects and actions for loading, adding and updating the Widgets in my ngrx store and component. The components are created based on this Widgets and can be a chart or datatable. And each of this widget should get the datastream (columns, rows, datapoins) from the backend.

My question is how can I implement this with ngrx? Should I load the complete data object from the backend to the widget item or what is the best approach for getting the data based on the Widget from my ngrx store.

1
How large is each widget and how many widgets are there? Also, how/whom/how often are these widgets updated? The answers to these drive whether you want to devise a lazy vs eager mechanism.MoMo
@MoMo The amount of the widgets differs. It can be from 1 to 30. The size of the datastream can be really big. For example a table with 60 columns and 10000 rows. Thats why I don´t know how to do that right as.Peter
And I have to use SocketIO (WebSockets) because I need the ability that the backend can update the clients if the data in the backend was modified.Peter
So a widget is max 600000 cells? How many bytes avg per cell (ie how many max bytes per widget)? Also, how many widgets DETAILS are visible at a time? Are all details visible at same time or scrollable? The UI also drives best approach. @peterMoMo
Also, please clarify if widget details are edited by clients or elsewhere? If you're socketing client changes are you managing edits optimistically (latest save wins) or pessimistically (check out/in)?MoMo

1 Answers

1
votes

@Peter, thanks for the clarifications. There are various ways to do this, however, based on the information you provided I believe the store state, in the context of Widgets and a Widget (of many datastream rows), will nominally be comprised of TWO slices (ie two SETS of reducers)--1: A store slice of an array of shallow widget objects where the datastream = null attribute in each, 2: A store slice of the current datastream being viewed/edited (with a widget identifier/reference), which itself is an array of row objects. I've pseudo'd each scenario below--I'm sure there are more or alternative/better variations. I assume you understand an ngrx store's reactive dispatch->action->effect->reduce flow and won't get into too much detail with that. I also assume your own or your 3rd party components' views get updated/changeDetected when a fresh copy of an object Widget or datastream row is provided (ie: changeDetection: ChangeDetectionStrategy.OnPush).

  1. Store slice: Widgets (ie: store.select('widgets')...): This store slice is an array of 'shallow' (datastream = null) widget objects. The angular component that contains these will dispatch a 'GET_SHALLOW_WIDGETS' action to hydrate the store slice upon instantiation. I presume the 'grid' component you mentioned will handle showing this store slice/array and also can interact with the store for CRUD operations such as 'UPDATE_SHALLOW_WIDGET', 'ADD_SHALLOW_WIDGET', 'DELETE_SHALLOW_WIDGET', etc.
  2. Store slice: Widget Datastream In View (ie: store.select('datastreamInView')...): This store slice is the one widget object datastream that is being viewed/edited. It is an array of 'widgetRow' objects where each represents one row in your data table (for a particular widget) which are presumably managed by ngx-datatable and hopefully 'changeDetectable'. Once a shallow widget ('1' above) is opened, the angular component housing your ngx-datatable will dispatch a 'GET_WIDGET_DATA' action to hydrate the array of widgetRow objects (slice: Widget Datastream In View) where each will be used to populate your ngx-datatable rows. Here you have a few choices for memory management:

    2A: Allow browser to manage memory heap and ask server for ALL data for the widget datastream. This isn't necessarily outrageous. You might find the browser handling 200MB (or more) of data with ease. If the bell curve of your retrieved data is in that range, I believe it's worth a shot. If you believe not:

    2B: Allow ngx-datatable to manage memory and push/pop array for scrolled out of view of rows. This obviously requires store/backend coordination. In other words, dispatch 'GET_PARTIAL_WIDGET_DATA' and pass an identifier to the 'last' row retrieved the previous time a 'GET_PARTIAL_WIDGET_DATA' action was dispatched. Twitter and Facebook use mechanisms akin to this. In this scenario the 'Datastream' store slice will only contain a subset of the datastream at any given time. CAUTION: Since you're maintaining an array, if you POP out the top elements of an array, a NEW array will be returned and you might get severe flickering. Hopefully your ngx-datatable manages this well and you don't have to worry about it. Otherwise, you need to ensure to write the pop in a way to not result in a mutated new array. Not so hard...plenty of examples in SO.

    2C: Manage smooth scrolling yourself as you suggested which is a variation of 2B in case ngx-datatable doesn't provide this.

Finally, since your view can get updated by a server socket.io trigger, here are a couple of scenarios to consider depending on the data the server sends:

  1. server sends 'WIDGET_UPDATED': This server msg contains a widget identifier and nothing more. If the widget in the msg is not the one you're editing, do nothing. On the other hand, if it is the one you're editing, you can allow user to dispatch their latest widget edits (optimistically) via a 'SUBMIT_WIDGET_DATA' store action without notifying them. You can also notify user and allow them to discard their edits and issue a 'GET_WIDGET_DATA' to update their data table view.
  2. server sends 'WIDGET_ROWS_UPDATED': this server msg is more granular and contains an array of rows that have been updated for a widget identifier. If the widget in the msg is not the one you're editing, do nothing. On the other hand, if it is the one you're editing, then the trigger will dispatch an 'UPDATE_WIDGET_ROWS' which will eventually result a reducer to find those row objects in that datastream array, delete and resurrect them with fresh data and trigger changedetection for the pertinent rows in your ngx-datatable.
  3. etc....

I hope this helps.