Technology Blogs by Members
Explore a vibrant mix of technical expertise, industry insights, and tech buzz in member blogs covering SAP products, technology, and events. Get in the mix!
cancel
Showing results for 
Search instead for 
Did you mean: 
former_member182650
Contributor

Why create a custom control

SAPUI5 offers several simple controls like TextField, Label... and really complex controls like ThingCollecion, Table, etc. All SAPUI5 controls are listed here:

https://sapui5.netweaver.ondemand.com/sdk/#content/Controls/index.html

It is not necessary create a custom control from scratch if:


sap.ui.commons.Button.extend("MyButton", { //inherit Button definition                                      
  metadata: {
    events: {
      "hover" : {}  //new event definition hover
    }
  },
  //hover event handler
  onmouseover : function(evt) {
    this.fireHover();
  },
  renderer: {} //Standard renderer method is not overridden
});






When our requirements doesn't fit standard SAPUI5 controls and we have no choice we can create custom controls.

What is a control and how it works

A control defines its appearance and behavior. All SAPUI5 controls extend from sap.ui.core.Control. In the other hand sap.ui.core.Element are parts of Controls but without renderer method. For example a Menu (Control) has different MenuItem (Element) and Menu renders its MenuItems.

Main structure of controls:

  • properties. Allows define its appearance and behavior on initialization.
  • aggregations. Lets group controls, variables, etc. Lets define some kind of containers inside a control. For example sap.ui.table.Table has different aggregations like columns, rows, etc.
  • associations. Controls can be associated with others that are not part of them. For example if we want to render a collection with next/prev functionality we could develop a previousItem / nextItem associations.
  • events. Control events should be related to higher level events more than standard DOM events (click, mouseover, etc). For example if we develop a Control which renders many tabs, tabSelect could be an event when a tab is selected.
  • appearance. Definition of our control in screen area. Every control has a render method in order to be rendered in HTML code.

My requirement: Custom autocomplete field like Google Gmail recipients.

I need a control that lets us:

  • add/remove different values
  • find values with autocomplete function
  • see all added values

Example Google Gmail recipients field:

Step by step

1. Create new library AutoCompleteValueHolder in new package /control:

2. Define a basic template on AutoCompleteValueHolder.js in order to test if it works:


sap.ui.core.Control.extend("control.AutoCompleteValueHolder", {
    metadata : {
      properties: {},
      aggregations: {},
      events: {}
    },
    init: function() {       
    },
    renderer : {     
        render : function(oRm, oControl) {
          oRm.write('Hello, this is a new control :)');
        }
    }
});






3. Load your control library in html file:


sap.ui.localResources('control');
jQuery.sap.require("control.AutoCompleteValueHolder");






4. Use your new control in a view:


var yourNewControl = new control.AutoCompleteValueHolder('yourNewControl');






5. Add custom properties, aggregations and events:


  metadata : {
    properties: {
      "codePropertyName": {type : "string", defaultValue: "code"}, //Define a model property representing an item code
      "descriptionPropertyName": {type : "string", defaultValue: "description"}, //Define a model property representing an item description
      "path": {type : "string", defaultValue: "/"}, //Define our model binding path
      "model": {type : "any", defaultValue: new sap.ui.model.json.JSONModel()} //Define our model
    },
    aggregations: {
      "_layout" : {type : "sap.ui.layout.HorizontalLayout", multiple : false, visibility: "hidden"} //Grouping of selected items and search text field
    },
    events : {    
        "selectedItem": {},
        "deletedItem": {},
        "deletedAllItems": {}
    }
  }
 






6. Initialize control:

This method will be called when an AutoCompleteValueHolder is instantiated


init: function() {
  var oControl = this;
  //Creation of search autocomplete field
  var searchField = new sap.ui.commons.AutoComplete(this.getId() + '-searchField',{
    maxPopupItems: 5,
    displaySecondaryValues: true,
    change: function change(event) {
      if (event.mParameters.selectedItem != null) { //If user selects a list item, a new item is added to _layout aggregation
    //Every new item consist in a TextField and a Image
        var newValueField = new sap.ui.commons.TextField({
          width: '100px',
          editable: false
        });
  //TextField shares model with other TextFields and Autocomplete. We select correct path (user selection)
        newValueField.setModel(event.getSource().getModel());
        newValueField.bindProperty("value", event.getSource().getParent().getParent().mProperties.descriptionPropertyName);
        newValueField.bindElement(event.mParameters.selectedItem.oBindingContexts.undefined.sPath);
        newValueField.addStyleClass('autoCompleteValueHolder_valueField');  //Custom style
  //Image let's us delete an Item. Press event will destroy TextField and Image from _layout aggregation
        var newValueImage = new sap.ui.commons.Image({
          src: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAYAAABWdVznAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3gIKEw06nF/eLQAAAb9JREFUKM9lkkFIFHEUxn//mf3TrJVOLjvu7phBBB0M3VyNTYg85CU6ZLfqmpAwRVBseqg1CI0OzZ47ePRkh9xQiAgWgqIOXUpNhHbBDGdFSxYX2m3+HZoNpe/yHt/73oP3vgfADHsxI8T/XCOZDuIkGHO9SWcMZKM2CnL+VMqZAGO3lgkw8rFDBe/+qMrbllsFsQhiNhZxy9kxlbetwmTQpANcgesnhy6MSF+iW9H0sw2vWtwf7u8aOHsvrCRmsvNI+e2H9Vl4rwOcgI/11dJBX5I+IJtQLU3nTCs6aDVH+L72lYVXr3NLlZ1Hb8DXp4A74C9Xay+P/9zaEeLXoFnXCdcV3nqR4ufFuw/LP7LP4fcUECrJENTqfAKqvo+/+o2atw2AsJqpKsVysOtmWxsCYAjkmXjkcVqoWx0b28xHTUBw3tuiZJm8U1puac3LPIUaALeP2c6Xnk5VAfXicEm3JXGw1M3MdtqqAWulLqlxvyvlnymUw3PZEYSHVpa4l4i4gADEcj7krfT3qSXu8MBCclXFNA+AqGDeP2je6dxkHyOnT/U437AMYF+Ivm5WhPW8wrGmMBIMaeC  wB5M5PywZXUzgAAAABJRU5ErkJggg==',
          press: function(event){
            var valueLayout = event.getSource().getParent();
            var autoCompleteHolderLayout = event.getSource().getParent().getParent().getParent().mAggregations._layout;
            autoCompleteHolderLayout.removeContent(valueLayout);
//Fire deletedItem event
          oControl.fireDeletedItem({
          allItems: oControl.getSelectedValues()
          });
          },
          width: '12px'
        });                        
        newValueImage.addStyleClass('autoCompleteValueHolder_valueImage'); //Custom style
  //Wrapping container for TextField and Image
        var valueLayout = new sap.ui.layout.HorizontalLayout({content: [newValueField, newValueImage]});
        valueLayout.addStyleClass('autoCompleteValueHolder_valueLayout');
  //Insert wrapping layout into 0 position
        event.getSource().getParent().getParent().mAggregations._layout.insertContent(valueLayout, 0);
        var content = event.getSource().getParent().getParent().mAggregations._layout.getContent();
//fire selectedItem event
oControl.fireSelectedItem({
          newItem: {
          code: event.mParameters.selectedItem.mProperties.additionalText,
          description: event.mParameters.selectedItem.mProperties.text
          },
          allItems: oControl.getSelectedValues()
          });
  //Reset value from autocomplete search field
        var search = content[content.length-1];
        search.setValue('');
      }
    }
  });
  searchField.addStyleClass('autoCompleteValueHolder_search'); //Custom style
  //_layout aggregation creation
  var layout = new sap.ui.layout.HorizontalLayout(this.getId() + '-valuesLayout',{allowWrapping: true});
  layout.addContent(searchField);
  layout.addStyleClass('autoCompleteValueHolder_valuesLayout');
  //Set _layout aggregation into our control
  this.setAggregation("_layout", layout);
}






7. Control rendering:

This method will produce html code:


renderer : {
  render : function(oRm, oControl) {
    var layout = oControl.getAggregation("_layout");
    layout.getContent()[0].setModel(oControl.getModel());
    var template = new sap.ui.core.ListItem({
      text: "{"+oControl.getDescriptionPropertyName()+"}",
      additionalText: "{"+oControl.getCodePropertyName()+"}"
    });
    layout.getContent()[0].bindItems(oControl.getPath(), template);
  oRm.write("<span");
    oRm.writeControlData(oControl);
    oRm.writeClasses();
    oRm.write(">");
    oRm.renderControl(layout); //Reuse standard HorizontalLayout render method.
    oRm.write("</span>");
  }
}






8. Custom methods

This custom methods lets us get selected items or clear all selected items:


getSelectedValues: function() {
  var content = this.getAggregation("_layout").getContent();
  var result = [];
  if (content != null && content.length > 1) {
    //Get all selected item into result
    for (var i=0; i<content.length-1; i++) {
      var model = content[i].getContent()[0].getModel();
      var path = content[i].getContent()[0].getBindingContext().sPath;
      result.push(model.getProperty(path));
    }
  }
  return result;
},
clearSelectedValues: function() {
  if (this.getAggregation("_layout").getContent() != null && this.getAggregation("_layout").getContent().length > 1) {
    //Delete all selected items (SubLayouts containing TextField+Image) from _layout aggregation
    while (this.getAggregation("_layout").getContent().length > 1) {
    this.getAggregation("_layout").removeContent(0);
//fire deletedAllItemsEvent
this.fireDeletedAllItems({});
  }
  this.getAggregation("_layout").rerender(); //ReRenders _layout aggregation
},
updateModel : function (newModel, newPath, codePropertyName, descriptionPropertyName) {
    this.setModel(newModel);
    this.setPath(newPath);
    this.setCodePropertyName(codePropertyName);
    this.setDescriptionPropertyName(descriptionPropertyName);
 
    var layout = this.getAggregation("_layout");  
    layout.getContent()[0].setModel(this.getModel());
      
        var template = new sap.ui.core.ListItem({
                                                 text: "{"+this.getDescriptionPropertyName()+"}",
                                                 additionalText: "{"+this.getCodePropertyName()+"}"
                                               });
        layout.getContent()[0].bindItems(this.getPath(), template);   
    }






9. Final result:

JS Bin - Collaborative JavaScript Debugging

EDIT (11/02/2014): Added some enhancements (three new events, and repetead item control).

Any suggestion or feedback will be welcome :smile:

Enjoy!

15 Comments
Labels in this area