1

I have a table that where the data is periodically updated by a javascript interval function in my controller:

var model = this.getview().getModel();  
var updateModel = setInterval(function(){
  model.loadData('path/to/my/data.json');  
}, 30000)  

This will basically be static display on a public monitor showing a summary of data.

I want to be able to highlight when a property has changed, so I've been trying to add a class to the control when it changes. The class will then highlight this in some way with CSS.

<Table items="{items}">  
  <columns>  
    <Column/>  
    <Column/>  
  </columns>  
  <items>  
    <ColumnListItem>  
      <cells>  
        <Text   
          text="{name}" />  
        <ObjectStatus  
          text="{value}"  
          state="{  
            path: 'value',  
            formatter: '.formatter.redOrGreen'  
          }"/>  
      </cells>  
    </ColumnListItem>  
  </items>  
</Table>  

So the model updates every 30 seconds. If the {value} field changes, I want to add a class to ObjectStatus control.

At the moment I'm just using a JSON model for local development to see if this is possible, but in production it will be an oData service.

JamieC
  • 567
  • 3
  • 11
  • You can `JSON.stringify()` your data and compare two strings - before and after the update. For example how to do that take a look at this post: [link](http://stackoverflow.com/questions/18050932/detect-differences-between-two-strings-with-javascript). – keshet Jan 29 '16 at 08:52
  • Maybe comparing the whole stringified data is not enough (since it doesn't highlight *what* has changed, only *how much*). I suggest comparing the last pulled data to the current data and binding the result to your view. http://stackoverflow.com/questions/8572826/generic-deep-diff-between-two-objects – Marc Jan 29 '16 at 15:02
  • Marc is right, this won't work for me as I need to know what has changed and how. I've managed to solve this similar to the answer to @schnoedel, which I'll write up in a separate answer. – JamieC Feb 01 '16 at 09:32

3 Answers3

2

Thanks for the answers, I managed to solve this, but my method wasn't quite covered by the answers on here. This is how I did it:

The requirements for this changed slightly since I posted the question. I'll need to indicate if something has changed, but also if the value has gone up or down. I'll also need to indicate if something goes above or below a certain value. I also wanted to make a solution that could be easily adapted if there are any other future requirements. This will also need to be easily adapted for oData when the backend service is up and running.

First of all (and key to this) is setting up a duplicate model, so this goes into my component.js file .I'm just duplicating the model here so that the values old and new values are unchanged, to make the formatter functions work on the first page load:

var oModel = new JSONModel('/path/to/data.js');
this.setModel(oModel, 'model');
this.setModel(oModel, 'oldModel');

In the controller for my view, I then take a copy of the old data, which goes into the old model that I've attached to the view, the new model is then updated. I do this in the after rendering hook to optimize the initial page load.

onAfterRendering: function(){
  var thisView = this.getView();
  var updateModel = function(){
    var oldData = thisView.getModel('model').getData();
    var oldModel = new JSONModel(oldWharehousesData);
    thisView.setModel(ollModel, 'oldModel');

    //update model
    var newModel = thisView.getModel('model');
    model.loadData('/path/to/data.js');

  };
  window.refershInterval = setInterval(updateModel, 30000);
}

I'm then able to input the new and old values to a formatter in my XML view and output a couple of custom data attribute:

<core:CustomData
    key="alert-status"
    value="{
      parts: [
        'model>Path/To/My/Property',
        'oldModel>Path/To/My/Property'
        ],
      formatter: '.formatter.alertStatus'
      }"
    writeToDom="true"/>
</customData>

My formatter.js :

alertStatus: function(newValue, oldValue){
  var alertNum = 25;
  if(newValue < alertNum && oldValue >= alertNum) {
    return 'red';
  } else if (newValue >= alertNum && oldValue < alertNum) {
    return 'green';
  } else {
    return 'none';
  }
}

I can then have as many custom data attributes as I like, run them through their own formatter function, which can be styled to my heart's content, e.g:

compareValues: function(newValue, oldValue) {
  if (newValue > oldValue) {
    return 'higher';
  } else if (newValue < oldValue){
    return 'lower';
  } else {
    return 'false';
  }
}
JamieC
  • 567
  • 3
  • 11
  • 1
    Interesting approach with the two models, even better than my diff-model suggestion because you can compare the values in your formatter. – Marc Feb 02 '16 at 13:14
  • 1
    Do you still have a Table/List? If so, did you find a possibility to use the aggregation list binding context for both models? Afaik using a list binding, you only have one context to one model. – schnoedel Feb 02 '16 at 20:40
  • It's still in a table, but not with aggregation binding. I wasn't using this in the original example either. It's giving me a slight headache thinking about how this could be done now though :) EDIT: Actually I did have this in the original example. The changes to to the requirements meant that this was actually not needed though. – JamieC Feb 02 '16 at 22:40
0

I have build an example on JSBin.

First you have to get the received data. You can use the Model.attachRequestCompleted event for that:

    this.model = new sap.ui.model.json.JSONModel();
    this.model.attachRequestCompleted(this.onDataLoaded, this);

In the event handler onDataLoaded you can retrieve the JavaScript object and compare it to a saved copy. You have to write the flags that indicate changes to the array item itself. (Storing it in a separate model as Marc suggested in his comment would not work because in your aggregation binding you only have the one context to your array item.)

At last you have to save the newData object as this.oldData for the next request.

  onDataLoaded:function(){
    var newData = this.model.getProperty("/");
    if (this.oldData){
      //diff. You should customize this to your needs.
      for(var i = 0, length = Math.min(newData.items.length, this.oldData.items.length); i< length; i++){
        newData.items[i].valueChanged = newData.items[i].value !== this.oldData.items[i].value;
        newData.items[i].nameChanged = newData.items[i].name !== this.oldData.items[i].name;
      }
    }
    this.oldData = newData;
    this.getView().getModel().setProperty("/",newData);
  },

You can then bind the ObjectState state property to the flag(s):

<ObjectStatus  
      text="{value}"
      state="{= ${valueChanged} ? 'Warning':'None' }"/>  

If you want to change the background color of the whole row or something like that you can apply Bernard's answer and use the flag(s) in a customData attribute.

Community
  • 1
  • 1
schnoedel
  • 3,918
  • 1
  • 13
  • 17
-1

You can use the <customData> tag This allows the insertion of a custom attribute into the HTML produced by the XML to HTML conversion process

In the example below for example I add a custom attribute (my own) - this code generates the following attribute data-colour in a relevant HTML element (a <SPAN> tag) - inspect the relevant element using, say, Chrome.

<customData>
   <core:CustomData writeToDom="true" key="colour" value="{vproducts>ListCostColour}" />
</customData>

You are then able to create a style for this attribute in your own style sheet as follows (and reference this in your manifest.json)

    [data-colour="red"] {
        background-color: #ffd1cc;
    }
    [data-colour="orange"] {
        background-color: rgba(255, 243, 184, 0.64);
    }
    [data-colour="green"] {`enter code here`
         background-color: rgba(204, 255, 198, 0.97);
     }
Bernard
  • 1,208
  • 8
  • 15