1
votes

I try to build an input field for quantities with plus and minus buttons. More like to understand how composite controls are working. I found this documentation.

I thought I could use the new control like this:

new ex.perimental.Input({
  width: "14em",
  editable: true,
  input: new sap.m.Input({
      width: "8em",
      value: {
          path: "model>Quantity",
          type: new sap.ui.model.type.Integer({}, {
            minimum:0
          })    
      },
      description: "{model>Unit}"
  }),
})

The control code looks like this:

sap.ui.define([
  "sap/ui/core/Control",
  "sap/m/Button",
  "sap/m/Input"
], function(Control, Button, Input) {
  "use strict";

  return Control.extend("ex.perimental.Input", {
    metadata: {
      properties: {
        width: {
          type: "string",
          defaultValue: "14em",
        },
        editable: {
          type: "boolean",
          defaultValue: false,
        },
      },
      aggregations: {
        _increaseButton: {
          type: "sap.m.Button",
          multiple: false,
          visibility: "hidden",
        },
        _decreaseButton: {
          type: "sap.m.Button",
          multiple: false,
          visibility: "hidden",
        },
        input: { type: "sap.m.Input", multiple: false },
        _hBox: { type: "sap.m.HBox", multiple: false, visibility: "hidden" },
      },
      events: {
        increase: {},
        decrease: {},
      },
    },

    _onDecrease: function(oEvent) {
      var oResourceBundle = this.getModel("i18n").getResourceBundle();
      var oldValue = this.getAggregation("input").getValue();
      var newValue = 0;
      if (!isNaN(Number(oldValue))) {
        newValue = Number(oldValue) - 1;
      }
      oInput.setValue(newValue);
      this.fireEvent("decrease", {
        oldValue: oldValue,
        newValue: newValue,
      });
    },

    _onIncrease: function(oEvent) {
      var oResourceBundle = this.getModel("i18n").getResourceBundle();
      var oldValue = this.getAggregation("input").getValue();
      var newValue = 0;
      if (!isNaN(Number(oldValue))) {
        newValue = Number(oldValue) + 1;
      }
      oInput.setValue(newValue);
      this.fireEvent("increase", {
        oldValue: oldValue,
        newValue: newValue,
      });
    },

    init: function() {
      this.setAggregation(
        "_decreaseButton",
        new Button({
          icon: "sap-icon://sys-minus",
          press: this._onDecrease.bind(this),
        })
      );
      this.setAggregation(
        "_increaseButton",
        new Button({
          icon: "sap-icon://sys-add",
          press: this._onIncrease.bind(this),
        })
      );
      this.setAggregation(
        "_hBox",
        new sap.m.HBox({
          items: [
            this.getAggregation("_decreaseButton"),
            this.getAggregation("_increaseButton"),
          ],
        })
      );
    },

    setEditable: function(sValue) {
      debugger;
      // aggregations will be null now
      // I assume because they are reused in the HBox control
      //    this.getAggregation("_increaseButton").setEditable(sValue);
      //    this.getAggregation("_decreaseButton").setEditable(sValue);
      //    this.getAggregation("input").setEditable(sValue);
    },

    setWidth: function(sValue) {
      this.getAggregation("_hBox").setWidth(sValue);
    },

    setInput: function(oInput) {
      this.setAggregation("input", oInput);
      var oHBox = this.getAggregation("_hBox");
      oHBox.insertItem(oInput, 1);
    },

    renderer: function(oRenderManager, oControl) {
      oRenderManager.write("<div");
      oRenderManager.writeControlData(oControl);
      oRenderManager.addClass("myStyle");
      oRenderManager.writeClasses();
      oRenderManager.write(">");
      oRenderManager.renderControl(oControl.getAggregation("_hBox"));
      oRenderManager.write("</div>");
    }

  });
});

It will be rendered but the setEditable is not working. The buttons (used inside the HBox control again) are not reachable via getAggregation. Also the input field (set from outside) can't be accessed.

Not sure how to do it right. Anyone an idea?

Edit2

This is the latest version but still not working. I am asking me how to put the externally defined input control into the right place inside the inner Hbox control and be able to access this control in methods like setEditable?

sap.ui.define([
  "sap/ui/core/Control",
  "sap/m/Button",
  "sap/m/Input"
], function(Control, Button, Input) {
  "use strict";

  return Control.extend("ex.perimental.Input", {
    metadata: {
      properties: {
        width: {
          type: "string",
          defaultValue: "14em",
        },
        editable: {
          type: "boolean",
          defaultValue: false,
        },
      },
      aggregations: {
        _hBox: { type: "sap.m.HBox", multiple: false, visibility: "hidden" },
      },
      associations: {
        input: { type: "sap.m.Input", multiple: false, singularName: "input" },
      },
      events: {
        increase: {},
        decrease: {},
      },
    },

    _onDecrease: function(oEvent) {
      var oResourceBundle = this.getModel("i18n").getResourceBundle();
      var oldValue = this._input.getValue();
      var newValue = 0;
      if (!isNaN(Number(oldValue))) {
        newValue = Number(oldValue) - 1;
      }
      this._input.setValue(newValue);
      this.fireEvent("decrease", {
        oldValue: oldValue,
        newValue: newValue,
      });
    },

    _onIncrease: function(oEvent) {
      var oResourceBundle = this.getModel("i18n").getResourceBundle();
      var oldValue = this._input.getValue();
      var newValue = 0;
      if (!isNaN(Number(oldValue))) {
        newValue = Number(oldValue) + 1;
      }
      this._input.setValue(newValue);
      this.fireEvent("increase", {
        oldValue: oldValue,
        newValue: newValue,
      });
    },

    init: function() {
      this._decreaseButton = new Button({
        icon: "sap-icon://sys-minus",
        press: this._onDecrease.bind(this),
      });
      this._increaseButton = new Button({
        icon: "sap-icon://sys-add",
        press: this._onIncrease.bind(this),
      });
      this.setAggregation(
        "_hBox",
        new sap.m.HBox({
          items: [
            this._decreaseButton,
            this.getAssociation("input"),
            this._increaseButton,
          ],
        })
      );
    },

    setEditable: function(sValue) {
      var bEditable = false;
      if (sValue === true) {
        bEditable = true;
      }
      this._decreaseButton.setEnabled(bEditable);
      this._increaseButton.setEnabled(bEditable);
      // seems not always working
      this._input.setEditable(bEditable);
    },

    setWidth: function(sValue) {
      this.getAggregation("_hBox").setWidth(sValue);
    },

    setInput: function(oInput) {
      this.setAssociation("input", oInput);
      this._input = oInput;
      var oHBox = this.getAggregation("_hBox");
      oHBox.insertItem(oInput, 1);
    },

    renderer: function(oRenderManager, oControl) {
      oRenderManager.write("<div");
      oRenderManager.writeControlData(oControl);
      oRenderManager.addClass("myStyle");
      oRenderManager.writeClasses();
      oRenderManager.write(">");
      oRenderManager.renderControl(oControl.getAggregation("_hBox"));
      oRenderManager.write("</div>");
    }

  });
});

I still have problems with the association handling (updated code) I guess handling the association should be done different? Sometimes the input field is still null.

1
It's an old thread but if someone still wonders how to build such a control: Take a look at sap.m.StepInput and the demo hereBoghyon Hoffmann

1 Answers

1
votes

a control can only be aggregated by one control at a time. This is the difference between associations (control may be at multiple at the same time) and aggregations.

What you can do in your init is:

this._decreaseButton = new Button (...)

Basically you only need one aggregation for your HBox. If your buttons are then aggregated by the HBox they will know the models of the parent and also be destroyed.

The only thing you need to check if your Root control is a Ancestor of the created controls (use myControl.getParent()).

best regards, Tobias