0

I have a component in SAPUI5 where I need to listen to changes in the binding (Binding.change event). At the moment I'm adding a listener to the Binding in modelContextChange like this:

function onModelContextChange(oEvent){
   var oSelect = oEvent.getSource();
   var binding = oSelect.getBinding("items");
   if(binding){
      binding.attachChange(onDataChange, oSelect);
   }
}

However this causes all kinds of weird problems, because modelContextChange could be fired multiple times. It would be better to to this in the XML view. I've tried code like this, but it doesn't work.

<Select items="{ path: 'project>/data/', change:'onDataChange' templateShareable: false">
   <core:Item key="{project>Id}" text="{project>Parameter}"/>
</Select>

Any recommendations how to do this?

taavilooke
  • 145
  • 1
  • 2
  • 11
  • Does this answer your question? [Perform Action after the Binding Is Complete](https://stackoverflow.com/questions/29770332/perform-action-after-the-binding-is-complete) – Boghyon Hoffmann Sep 08 '22 at 22:58

4 Answers4

3

I just came across the problem, too. I solved it in that way:

<Select items="{
  path: 'project>/data',
  events: {
    change: '.onDataChange'
  },
  templateShareable: false
}">

You can pass an events object to the binding and use the available bindings (e.g. v2.ODataListBinding). Note that you have to use a dot before your function name (.onDataChange). In your controller you can add the function:

onDataChange: function(oEvent) {
  // Your implementation...
},
Boghyon Hoffmann
  • 17,103
  • 12
  • 72
  • 170
Matthias Günter
  • 617
  • 8
  • 24
1

If I remember well from the JS Views, I think it is like this:

<Select items="{ path: 'project>/data/', events: {change:'onDataChange'}, templateShareable: false}">

This is for listening to the Model "change" events.

If you want to listen to the "change" event in the Select control, this is when the user selects a different value in the dropdown, it is like this:

<Select items="{ path: 'project>/data/', templateShareable: false}" change="onDataChange">

EDIT: Using "modelContextChange" event.

<Select items="{ path: 'project>/data/', templateShareable: false}" modelContextChange="onDataChange">
Rafael López Martínez
  • 2,225
  • 2
  • 15
  • 23
  • Thanks, the _events_ property seems to do exactly what I was looking for, but there's still one thing missing. I need to pass the Select component itself to the onDataChange as well. As it is, the event handler gets just the binding as `event.getSource()` and the controller as `this`. With attachChange it's easy to change it because I can pass the Select as this, but how do I do it through the XML? Or maybe there's some other way to access the Select inside the onDataChange? – taavilooke Jul 13 '18 at 11:27
  • I think you are mixing things. There are 2 "change" events. One is the event fired by the control, whenever the selection has changed. With this you have the reference to the control with the `event.getSource()`. Then you have the "change" event fired by the binding. With this you are suscribing for changes in the model in the specific path, so you have the reference to the model, but not for the control. This last one has the advantage/disadvantage of being fired everytime a value is change in this node of your model, it doesn't matter if it is changed in the Select control or not – Rafael López Martínez Jul 13 '18 at 11:52
  • If you want to watch the changes within the control, you need to listen from the "change" event in the control, as I mentioned in the second snippet of my answer – Rafael López Martínez Jul 13 '18 at 11:53
  • Let me explain: the thing I'm trying to do in the end is to bind the Select's items to a list of entries in the JSONModel (_project>/Parameters/_), but have one more option (called _Inherit_) in front of all the other entries. I tried creating a separate model for the select's items, but this creates the problem of keeping the two in sync. So what I'm looking for is that, when the bound list is changed, I could catch this event and add the _Inherited_ back in from of all the other entries. – taavilooke Jul 13 '18 at 13:54
  • I manage to get it _kind of_ working by adding the Binding change event listener in the _modelContextChange_ event ([see this snippet](https://bitbucket.org/snippets/taavilooke/oeGLrA)). This mostly works, but I have to check multiple thing, because _modelContextChange_ could be fired multiple times before and after the binding is made. So I'm looking for a way to attach this Binding change event directly in the XML, so I can get rid of all those weird check I have to do. – taavilooke Jul 13 '18 at 14:05
  • Then for that you need to suscribe to the "modelContextChange" event in the control. Then you can use the getSource(). It is define in a parent control of the Select control, [here the documentation](https://sapui5.hana.ondemand.com/#/api/sap.ui.base.ManagedObject/events/modelContextChange). I have edited my answer with the snippet you need – Rafael López Martínez Jul 13 '18 at 15:21
  • I think it will be fired less times than the model "change" event, so it will be event easier to manage – Rafael López Martínez Jul 13 '18 at 15:23
  • No, not really. The way I got to looking into the Binding change event was that _modelContextChange_ is not fired every time the contents of the model change, actually it's seems that it's never fired when the content changes, only when the you change the binding itself, somthing like calling _bindItems()_. I could fake it by calling fireModelContextChange() on it every time it needs to be updated, but that seems quite hackish. – taavilooke Jul 16 '18 at 06:09
  • I put a JSBin together to show that adding items to the databound model does no fire _modelContetChange_. [Click on this.](http://jsbin.com/wiqoragaju/edit?html,js,console,output) – taavilooke Jul 16 '18 at 06:42
  • I still think that the concepts are being mixed. You have your model in one side, with has nothing to do with the Select control. The select control is linked with the model, so the framework updates how the control is rendered if the model change. But, from the model you cannot get an specific control. So if you want to do an specific action in a specific control of your view, whenever there is a change in your model, I guess you should store the references to your controls and select the one you want from the list for the specific model change – Rafael López Martínez Jul 17 '18 at 02:54
0

here is an example

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>JS Bin</title>
    <script 
            src="https://openui5.hana.ondemand.com/resources/sap-ui-core.js" 
            id="sap-ui-bootstrap" 
            data-sap-ui-theme="sap_bluecrystal" 
            data-sap-ui-xx-bindingSyntax="complex" 
            data-sap-ui-libs="sap.m"></script>
    <style type="text/css">
      .sapMObjLTitle {
        cursor: pointer;
      }
    </style>

    <!-- XML-based view definition -->
    <script id="oView" type="sapui5/xmlview">
    <mvc:View height="100%" controllerName="myView.Template"
      xmlns="sap.m"
      xmlns:core="sap.ui.core"
      xmlns:mvc="sap.ui.core.mvc">
      <Select change="onDataChange" items="{ path: 'project>/data', templateShareable: false}">
        <core:Item key="{project>Id}" text="{project>Parameter}"/>
      </Select>
    </mvc:View>
    </script>

  </head>
  <body class="sapUiBody">
    <div id='content'></div>
  </body>
</html>


sap.ui.define([
  'jquery.sap.global',
  'sap/ui/core/mvc/Controller',
  'sap/ui/model/json/JSONModel'
], function(jQuery, Controller, JSONModel) {

  var ListController = Controller.extend("myView.Template", {
    onInit: function(oEvent) {
      var oView = this.getView();
      oView.setModel(new JSONModel({
        data: [{
          Id: "1",
          Parameter: "One" 
        }, {
          Id: "2",
          Parameter: "Two" 
        }]
      }), "project");
    },
    onDataChange: function() {
      alert("changed")
    }
  });

  return ListController;
});

// Instantiate the View and display
var oView = sap.ui.xmlview({
  viewContent: jQuery('#oView').html()
});

oView.placeAt('content');

https://jsbin.com/zufurav/edit?html,js,output

Note: the change attribute in your XML is incorrect

D. Seah
  • 4,472
  • 1
  • 12
  • 20
0

I managed to get the binding change event with this set to the element that I needed by attaching a modelContextChange to the element and handling attaching the change event to the binding in there. Here's the code from the view controller:

modelContextChange: function(oEvent) {
  var oElement = oEvent.getSource();
  var binding = oElement.getBinding("items");
  if (binding) {
    //Binding change event could already be attached, detach it first, if it's there
    binding.detachChange(this.onBindingChange, oSelect);
    binding.attachChange(this.onBindingChange, oSelect);
    // Beacause I used this inside a sap.ui.table.Treetable,
    // in some cases binding changes occur without the binding change event firing.
    // Manually fire the binding change just in case
    // YMMV
    binding.refresh(true);
  }
},
onBindingChange: function() {
  //The code that needs to run at binding change
  //"this" is set to the correct element 
  //I specifically needed to add one element in front of other items in a sap.m.Select
  var items = this.removeAllItems();
  this.addItem(new sap.ui.core.Item({
    key: 0,
    text: "< Inherit >"
  }));
  items.forEach(function(item) {
    this.addItem(item);
  }, this);
}
taavilooke
  • 145
  • 1
  • 2
  • 11