javascriptmodelsapui5custom-controlsbinding-context

SAPUI5 - How can I get the name of the model to which my Custom Control has been bound?


I am working on a Custom Control in SAPUI5. The goal is a Pie Chart with clickable segments. Whenever a segment is clicked, an Event should be fired which contains the binding context of the clicked segment (e.g. "/testdata/2"). The context is used by attached functions to know which segment has been clicked (and e.g. display its name in a Label).

Since the function getBindingContext always requires a modelname (or undefined) to extract the current context, I had to hardcode the modelname ("usage") during testing to get it to work. However later (when this Control gets used by others) that modelname will obviously differ from the one I used here. So how can I get the current binding context without knowing which name the bound model has?

The current (relevant) implementations are as follows:

Example XML View:

<demo:PieChart id="testchart" segments="{usage>/testdata}">
    <demo:Segment color="{usage>color}" size="{usage>size}"/>
</demo:PieChart>
<Label id="testlabel" text="{usage>name}"/>

Test Data (usage.json):

{
    "testdata": [
        {
            "name": "test_name_1",
            "size": 123,
            "color": "rgb(100,140,100)"
        },
        ...
    ]
}

Inside "Segment":

onclick: function(Event) {
    // Hard-coded modelname inside Control, this won't work later :/
    var context = this.getBindingContext("usage").getPath();
    this.getParent().fireSegmentClick({context: context});
}

Inside "PieChart":

metadata: {
    ...
    aggregations: {
        segments: {
            type: "demo.Segment"
        }
    },
    defaultAggregation: "segments",
    events: {
        segmentClick: {
            allowPreventDefault: true
        }
    }
}

Example attachment in the controller:

onInit: function() {
    var chart = this.byId("testchart");
    var label = this.byId("testlabel");
    chart.attachSegmentClick(function(Event) {
        var context = Event.getParameter("context");
        // The user knows the modelname here (he bound it).
        // But how do I get that name inside the Control?
        label.bindObject({path: context, model: "usage"});
    });
}

My best try: Inside the segment's onclick-function I can call

var model = this.getParent().getBinding("segments").getModel();

This gets me the model I want. But since neither model.toString() or model.getName() can get me its name, this is of no use (because getBindingContext() doesn't want the model itself, but its name...)

Any help is appreciated!

PS: Some (hopefully) helpful links:

Binding Reference

getBindingContext Reference

Related first answer


Solution

  • So how can I get the current binding context without knowing which name the bound model has?

    The problem is that a control (or ManagedObject) can store multiple models at once, all with different names. So even if the control "knows" all the model names, it will be hard to guess even which one of the available bound contexts the application wants as there can be zero or many of them.

    Technically, it's surely possible as krisho's answer suggests. But in my opinion, it's not control's responsibility to propagate bound context(s) to the application. As you mentioned, only the application knows all the models and their names, thus, what kind of context it wants. Below is my suggestion:

    Controls

    Segment

    onclick: function(oEvent) {
      const parent = this.getParent();
      if (parent && parent.isA("demo.PieChart")) {
        parent.fireSegmentClick({
          segment: this, // deliver the clicked segment instead of some context(s)
        });
      }
    },
    

    PieChart

    metadata: {
      //...
      aggregations: {
        segments: {
          type: "demo.Segment",
        },
      },
      defaultAggregation: "segments",
      events: {
        segmentClick: {
          allowPreventDefault: true,
          parameters: {
            segment: {
              type: "demo.Segment",
            },
          },
        },
      },
    },
    

    Application

    onSegmentClick: function(event) {
      const segment = event.getParameter("segment");
      const boundContext = segment.getBindingContext(/*I know the model name*/);
      // ...
    },
    

    This enables loose coupling between controls and application/models.