Search code examples
sapui5

How to Request Entries Automatically in x Seconds Intervals


In an XML view, I present a sap.m.Table with entries from an output ODataModel. Now the first 8 entries should be output for about 5 seconds and the remaining entries automatically reload, and switch in 5 second intervals, like a dashboard.

Here is an example: I have 30 entries.

  1. At the beginning → Show entries 1 to 8.
  2. After 5 seconds → Show entries 9 to 16.
  3. After next 5 seconds → Show 17 to 24.
  4. After next 5 seconds → Show 25 to 30.
  5. Then repeat from the beginning.

Would you have any idea how I can do that? Could it possibly realized on the routing function with transfer of the required parameters to the URL?


Solution

  • I have made a Plunker to demonstrate how this can be achieved: Plunker Demo.

    In a nutshell, you can leverage the startIndex and length parameters of the ManagedObject#bindAggregation method. More specifically, you use them to bind the table repeatedly.

      this.byId("list").bindItems({
        path: "/Meetups",
        template: this.byId("template"),
        templateShareable: true,
        startIndex: start,
        length: SLICE_SIZE
      });
    

    Because you continually rebinding the table, you can simply create the template object inside the XML view as a dependent, then you can access it via this.byId(...). We need to use the templateShareable flag to indicate that the template will be reused in further bindings (on the next tick).

    <dependents>
      <StandardListItem id="template" title="{Title}" 
        description="{Description}" counter="{MeetupID}" />
    </dependents>
    

    Regarding the routing, I made two routes: one initial ("default") route and one route which specifies the start index ("withStart"). Both of them pointing of course to the same view.

      "routes": [{
        "pattern": "",
        "name": "default",
        "target": "main"
      }, {
        "pattern": "/{start}",
        "name": "withStart",
        "target": "main"
      }]
    

    The transition between "slices" is done with the help of jQuery.sap.delayedCall. You can also use a IntervalTrigger instead, but it might cause some problems if the view is not the first one shown (as the first "slice" might not be shown for the full 5 seconds, depending of course on how you implement it).

    jQuery.sap.delayedCall(INTERVAL, this, this.onTick, [start]);
    

    Then a tick happens (i.e. when we need to change the "slice"), we simply do a navigation to the withStart route and increment the start index parameter. At this point, we can also check if we need to start from zero again (if the start index is greater than the total count).

      start += SLICE_SIZE;
      if (this.count && start >= this.count) {
        start = 0;
      }
      this.getOwnerComponent().getRouter().navTo("withStart", {start: start});
    

    In order to find out the total count (to be able to determine if you should go to zero), you can use the information from the List / Table's ListBinding.

        var oBinding = this.byId("list").getBinding("items");
        if (oBinding.isLengthFinal()) {
          this.count = oBinding.getLength();
        }
    

    One problem that you might face in a multi-view application is that, if the user navigates between your "dashboard" view and some other view, the delayedCall might cause you to navigate back again to the "dashboard" view (i.e. the user might get "trapped" in the dashboard). To circumvent this, you could check to see if the view is visible first, before doing the navTo call.


    Later edit:

    For improving performance due to loading times you can use two different approaches:

    1. Use the operation mode parameter when you are doing the binding. Setting it to Client will load all entries at once on the client side and the pagination mechanism will be done without further requests (at least in theory).
      this.byId("list").bindItems({
        path: "/Meetups",
        template: this.byId("template"),
        templateShareable: true,
        startIndex: start,
        length: SLICE_SIZE,
        operationMode: OperationMode.Client
      });
    
    1. Do an initial load of the data using the ODataModel.read method, store the results into a JSON Model and then use the JSON Model for binding the list instead of the OData Model.
    var oModel = this.getView().getModel("myJsonModel");
    this.getView().getModel().read("/Meetups", {
        success: function(oData) {
            oModel.setData(oData);
        }
    });