0
votes

I build a control like this:

sap.ui.define([
    "sap/m/VBox",
    "sap/m/Label",
    "sap/m/Input",
], function (VBox, Label, Input) {
    "use strict";
    return VBox.extend("my.special.Control", {
        metadata : {
            properties : {
            },
            events: {
            },
        },
        init : function () {
            var that = this;
            if (VBox.prototype.init) {
                VBox.prototype.init.call(this);
            }
            this.addItem(new Label({text : "label"}));
            this.addItem(new Input({value : "value"}));
        },
        renderer : {},
    });
});

During usage it looks like the control duplicates the elements (Label and Input). I assume the duplication will be done somehow inside the SAPUI5 framework.

An example with the problem you will find here: https://jsbin.com/fuxenezije/1/edit?html,console,output

My intention is to extend the control with some more complex functions but the posted example will show just the minimum idea of reusing the existing controls inside a new one.

Anyone an idea if I did something wrong here or could it be a bug in the UI5 framework. For me it looks like this happens in combination of FlexBox inside a Grid control.

If I provide the two controls outside like this

template : new my.own.Control({
   items : [ new sap.m.Label({text:"l"}), new sap.m.Input()]
})

nothing will be duplicated.

https://jsbin.com/dilohacudu/1/edit?html,console,output

What could be wrong with my code?

2
probably your init method is called twice (check it with some trace). You should use aggregations for inner controls :)Ji aSH
I added already a breakpoint there but this.getIems() was always empty in init. Do you have an example how to create such controls (Input, Label) inside my.own.Control and put it into the items collection of the VBox?user3783327
Looks like init is only called once.user3783327
I just put a console.log() in your code snippet and I can see the log several times, maening your init is called several times :) On the other hand that 'init' sounds suspect to me (arent we supposed to use onInit() ?). Ill post a sample using aggregations !Ji aSH
Would be nice. I set a breakpoint on a local project and checked if this.getItems() contains any data before the addItem call but it was always empty. init is called three times because of the binding to the example model with three entries. But would be nice to see a working code :)user3783327

2 Answers

1
votes

and here it comes

sap.ui.define("my/own/Control",[
    "sap/m/VBox",
    "sap/m/Label",
    "sap/m/Input",
], function (VBox, Label, Input) {
    "use strict";
    var Control = VBox.extend("my.own.Control", {
        metadata : {
            properties : {
                label : {
                    type: 'string',
                    defaultValue: 'kiko'
                },
                value : {
                    type: 'string', 
                    defaultValue: 'lol'
                }
            },
            aggregations : {
                _label: {
                    type: 'sap.m.Label',
                    multiple: false,
                    visibility: 'hidden'
                },
                _value : {
                    type: 'sap.m.Input',
                    multiple: false,
                    visibility: 'hidden'
                }
            }
        },
        renderer : function(oRm, oControl) {
            oRm.write('<div');
            oRm.writeControlData(oControl);
            oRm.writeClasses();
            oRm.write('>');
            oRm.renderControl(oControl.getAggregation('_label'));
            oRm.renderControl(oControl.getAggregation('_value'));
            oRm.write('</div>');
        }
    });

    Control.prototype.init = function() {
        VBox.prototype.init.call(this);
        this.setAggregation('_label', new Label({ text: this.getProperty('label') }));
        this.setAggregation('_value', new Input({ value: this.getProperty('value') }));
    };

    Control.prototype.setLabel = function(l) {
        this.setProperty('label', l, true)
        this.getAggregation('_label').setText(l);
    };

    Control.prototype.setValue = function(v) {
        this.setProperty('value', v, true)
        this.getAggregation('_value').setValue(v);
    };

    return Control;
});

You should of course remove the default values :p

you can now create your control with new Control({label:'mylabel', value:'myvalue'}, use the setters to rerender, and leverage the binding from sapui5

0
votes

I met a similar issue. Then I found this post.

The only difference is I'm using the custom control in a table cell. Later I found that even I only have 5 rows in the table, the init function will be invoked 6 times.

So the reason is, the custom control will be initialized once as a template, then the init function will be called again when creating instance in each row. So the content will be duplicated.

The way I solve the issue, is put this.addItem function in the renderer, just before the rendering. Since the template will not be rendered, this content will be only added once.