Quantcast
Channel: SAPUI5 Developer Center
Viewing all articles
Browse latest Browse all 789

How to create custom control from scratch

$
0
0

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:

 

sh000.png

 

Step by step

 

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

 

sh001.PNG

 

 

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: {}    },    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');

 

sh002.PNG

 

 

 

5. Add custom properties and aggregations:

 

 

  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    }  

 

 

 

6. Initialize control:

 

 

This method will be called when an AutoCompleteValueHolder is instantiated

 

 

init: function() {   //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+/+o2atw2AsJqpKsVysOtmWxsCYAjkmXjkcVqoWx0b28xHTUBw3tuiZJm8U1puac3LPIUaALeP2c6Xnk5VAfXicEJdam3JXGw1M3MdtqqAWulLqlxvyvlnymUw3PZEYSHVpa4l4i4gADEcj7krfT3qSXu8MBCclXFNA+AqGDeP2je6dxkHyOnT/U437AMYF+Ivm5WhPW8wrGmMBIMaeBCI/wB5M5PywZXUzgAAAABJRU5ErkJggg==',          press: function(event){            var valueLayout = event.getSource().getParent();            var autoCompleteHolderLayout = event.getSource().getParent().getParent().getParent().mAggregations._layout;            autoCompleteHolderLayout.removeContent(valueLayout);          },          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();  //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);  }  this.getAggregation("_layout").rerender(); //ReRenders _layout aggregation
}

 

 

9. Final result:

 

JS Bin - Collaborative JavaScript Debugging

sh003.PNG

 

sh004.PNG

 

 

 

Any suggestion or feedback will be welcome

Enjoy!


Viewing all articles
Browse latest Browse all 789

Trending Articles