0

I am currently working on single Tiles and want to add some count values from the Northwind OData service. The app contains just one view and one controller.

View

<mvc:View
  controllerName="customtileapp.controller.CustomTile1"
  xmlns="sap.m"
  xmlns:mvc="sap.ui.core.mvc"
>
  <GenericTile
    class="sapUiTinyMarginBegin sapUiTinyMarginTop tileLayout"
    header="Country-Specific Profit Margin"
    subheader="Expenses" press="press"
  >
    <TileContent
      unit="EUR"
      footer="Current Quarter"
    >
      <NumericContent
        scale="M"
        value="{
          path: '/Customers',
          formatter: '.formatTile'
        }"
        valueColor="Error"
        indicator="Up"
        formatterValue="true"
      />
    </TileContent>
  </GenericTile>
</mvc:View>

Controller

sap.ui.define([
  "sap/ui/core/mvc/Controller",
  "sap/m/MessageToast",
  "sap/ui/model/odata/v2/ODataModel"
], function (Controller, MessageToast, ODataModel){
  "use strict";

  return Controller.extend("customtileapp.controller.CustomTile1", {
    onInit: function() {
      this.oView = this.getView();
      this.oModel = new ODataModel("/northwind/V2/Northwind/Northwind.svc");
      this.oView.setModel(this.oModel);
    },

    formatTile: function() {
      var counter;
      this.oModel.read("/Customers/$count", {
        async : true,
        success : function(oData, response) {
          counter = response.body;
          MessageToast.show(counter);
        }
      });
      return counter;
    }
  });
});

The MessageToast inside the formatter Function works fine and shows the correct number of customers ("91"). But the number I want to show on the tile always shows "0".

Tile

Boghyon Hoffmann
  • 17,103
  • 12
  • 72
  • 170
Timur
  • 169
  • 4
  • 13

3 Answers3

1

This is a very different approach to what you are trying to achieve. However here is some information I could derive to get this:

enter image description here

  1. Why value returned by formatter is not showing up in binding ? Ans : This will not show up! Why : Binding at view does not wait for the formatter function to return the value. The read request performed in formatter is 'async' by default. However, even if it had to work synchronously, you could try something like this :

    formatTile: function() { var counter; this.oModel.read("/Customers/$count", {async : true, success : function(oData, response) { counter = response.body; return counter;

    }}); }

But this won't work as well as read may take a while and the binding would not wait.

  1. Updating the $count is typically used in List and Table headers. Where it works as follows (which I also applied as a workaround)

There shall be an event trigger(onUpdateFinishedMaster in my example) in order to get the count of Customers and then the value in NumericContent can be updated.

Controller:

sap.ui.define(['sap/m/MessageToast', 'sap/ui/core/mvc/Controller'],
 function(MessageToast, Controller) {
  "use strict";

  return Controller.extend("tilesGenericTIles.controller.View1", {
   onInit: function() {
    this.oView = this.getView();
    this.oModel = new sap.ui.model.odata.v2.ODataModel("/destinations/northwind/V2/Northwind/Northwind.svc/", true);
    this.oView.setModel(this.oModel);
   },
   formatTile: function(sCount) {
    
    var counter;
    this.oModel.read("/Customers/$count", {
     async: true,
     success: function(oData, response) {
      
      counter = response.body;
      return counter;
      MessageToast.show(sCount);
     }
    });
    
    return 'test' ;

   },
   onUpdateFinishedMaster: function(oEvent){
    // 
    var count,
    oTable = oEvent.getSource();
    var iTotalItems = oEvent.getParameter("total");
    
    this.getView().byId("idNumericContent").setValue(iTotalItems);
   }

  });
 });

View:
<mvc:View controllerName="tilesGenericTIles.controller.View1"  xmlns:l="sap.ui.layout" xmlns:html="http://www.w3.org/1999/xhtml" xmlns:mvc="sap.ui.core.mvc"
 displayBlock="true" xmlns="sap.m">
 <App>
  <pages>
   <Page title="{i18n>title}">
    <content>
     <l:VerticalLayout>
      <GenericTile class="sapUiTinyMarginBegin sapUiTinyMarginTop tileLayout" header="Country-Specific Profit Margin" subheader="Expenses"
       press="press">
       <TileContent unit="EUR" footer="Current Quarter">
        <NumericContent scale="MM" value="{path: 'Customers', formatter: '.formatTile'}" valueColor="Error" indicator="Up" id="idNumericContent"/>
       </TileContent>
      </GenericTile>
      <Table id="idMasterTable" width="auto" items="{ path: '/Customers'}" noDataText="{i18n>masterTableNoDataText}"
       busyIndicatorDelay="{worklistView>/tableBusyDelay}" growing="true" growingScrollToLoad="true" updateFinished="onUpdateFinishedMaster"
       mode="SingleSelectLeft" inset="false" selectionChange="onMasterTableSelectionChange">
       <columns>
        <Column vAlign="Middle" id="idColumnAppGrp">
         <header>
          <Text text="{Customer Name}"/>
         </header>
        </Column>
        <Column vAlign="Middle" id="idColumnAppGrp1">
         <header>
          <Text text="{Customer Name}"/>
         </header>
        </Column>
       </columns>
        <items>
         <ColumnListItem type="Navigation" press="handleMasterPress" tooltip="{i18n>masterColumnItemTooltip}">
          <cells>
           <ObjectIdentifier title="{ContactName}"/>
           <ObjectIdentifier title="{ContactName}"/>
          </cells>
         </ColumnListItem>
        </items>
       </Table>
      </l:VerticalLayout>
     </content>
    </Page>
   </pages>
  </App>
 </mvc:View>

Update Count on button press

  1. Add a button beside the generic tile:

      <l:HorizontalLayout>
      <GenericTile class="sapUiTinyMarginBegin sapUiTinyMarginTop tileLayout" header="Country-Specific Profit Margin" subheader="Expenses"
       press="press">
       <TileContent unit="EUR" footer="Current Quarter">
        <NumericContent scale="MM" value="{path: 'Customers', formatter: '.formatTile'}" valueColor="Error" indicator="Up" id="idNumericContent"/>
       </TileContent>
      </GenericTile>
      <Button text="updateCount" press="updateCount" />
      </l:HorizontalLayout>
  1. Update count of the tile on press event.

   updateCount: function(oEvent){
     
    var counter;
    this.oModel.read("/Customers/$count", {
     async: true,
     success: function(oData, response) {
      
      counter = response.body;
      this.getView().byId("idNumericContent").setValue(counter);
     }.bind(this)
    });

Additionally, the function "updateCounter" can also be called periodically with a timer.

Kindly let me know if this helps.

Nandan Chaturvedi
  • 1,028
  • 3
  • 16
  • 32
  • Dear Nanda, thanks a lot! It works now :-) So, am I right that without the table I could not get the counter to the tile? Because I want to show the Tile in a Firoi Launchpad (via Portal) and do not really need to show the table (just need the tile :)) – Timur May 13 '18 at 22:04
  • @Timur As Nandan mentioned, the main issue is that the `formatTile` returns `undefined` since the GET request, triggered by the `read` method, is sent asynchronously. The formatter doesn't wait until the client receives the count value. You can always update the count *manually* without a table (ListBinding). – Boghyon Hoffmann May 14 '18 at 02:30
  • @Timur , you should update the count manually on any event as I've mentioned above . thanks Boghyon for adding more clarification on this . – Nandan Chaturvedi May 14 '18 at 03:16
  • @BoghyonHoffmann thanks for your help! But how can I do it without any table or list, I don't really get it :( – Timur May 14 '18 at 16:08
  • Here is what I did as an example to update the count manually: fire the press event of a button and execute the code of your formatter. Check updated answer for this. – Nandan Chaturvedi May 14 '18 at 16:36
  • Hi Nandan, thanks a lot! It now works fine :) I used the code from Graham above. But your feature is very cool. I will also use it in my Application – Timur May 14 '18 at 18:51
1

Similar to the earlier response from Nandan, I usually use a standalone method to get the value for a tile.

        getTileValue: function () {
            var oTileValue = this.myView.byId('tileValue');
            this.getModel().read('/Dealers/$count', {
                success: $.proxy(function (oEvent, oResponse) {
                    var count = Number(oResponse.body);
                    oTileValue.setValue(count);
                }, this)
            });
        },

I like this because I can setup a timer to regularly update the counter.

Graham Robbo
  • 211
  • 2
  • 6
0

If you were working with a JSON model (not OData) you could solve this through expression binding right within your XML view. No need for any fancy controller math:

  <NumericContent
    ...
    value="{= ${/Customers}.length }"
    ...
  />

See this SO answer for all your options to count records in different SAPUI5 data models (JSON, OData v2, OData v4).

Boghyon Hoffmann
  • 17,103
  • 12
  • 72
  • 170
Jpsy
  • 20,077
  • 7
  • 118
  • 115