Skip to Content
Technical Articles

Creating a Sliding Panel control with SAPUI5 – Part 1

Hello,

I am back with a handy SAPUI5 control/extension blog post. Hope this helps ๐Ÿ™‚

This is a bigger one so I will probably split it in 2 parts – so stay tuned for the following part and be sure to follow me, to get an update when they are ready.

I was designing a mobile app when I came across a thing I am missing among the tons of SAPUI5 ready controls: a panel, that I can slide in and out of view. As just mentioned this is probably more mobile applicable than desktop, but of course this could be used for either – we stay true to Fiori and make this new/extended control responsive!

 

Intro / Use Case

My use case is following:
On a mobile app, I want a menu that is by default not visible but can be slid into view through a button. Once I’m done with the menu, with the same button I can slide it out of view again (besides the button of course).

Important notes:

  • I want my control to render the full menu, including the button needed for the sliding
  • I want my control to take care of the sliding
  • I want my control to adjust to responsive widths

 

My Solution Design

To achieve a sliding panel like this, I created following solution design:

  1. We have a main wrapper (red border above), that is transparent, so that only the button is visible, when the panel is slid away (otherwise you would have a thick bar below the button in the height of the menu, also always visible so this is important)
  2. Inside the wrapper we have a toolbar (green above), which is as wide as the wrapper and this toolbar, will hold the button, that will perform the sliding action
  3. Lastly we have the content, which has a width dependent on the size of the wrapper, minus the button width including all margins and paddings

 

1. Custom Control

As said there is no control in UI5 that does all of the above but we can still use a standard control and extend it, instead of reinventing the wheel: I decided for sap.m.Panel

Panel is a very simple control and is in fact a wrapper that can have a header (and info) toolbar. Therefore, perfect for my use case.

 

1.1. Start Extending sap.m.Panel

We need a JS file for our custom control, for example: PROJECT/webapp/controls/SlidePanel.js

Starting with the absolute minimum and then we fill the code up one-by-one:

sap.ui.define([
	"sap/m/Panel",
	"sap/ui/Device",
	"sap/ui/layout/VerticalLayout",
	"sap/m/Toolbar"
], function (Panel, Device, VerticalLayout, Toolbar) {
	"use strict";
	
	return Panel.extend("NAMESPACE.controls.SlidePanel", {
		
		/* Add new property */
		metadata: {
			properties: {
			}
		},
		
		/* =========================================================== */
		/* lifecycle methods                                           */
		/* =========================================================== */
		
		/* Initialisation */
		init: function() {
			// call standard initialisation
			if(Panel.prototype.init) {
				Panel.prototype.init.apply(this, arguments);
			}
		}, 
		
		/* Changes before rendering... */
		onBeforeRendering: function() {
			if(Panel.prototype.onBeforeRendering) {
				Panel.prototype.onBeforeRendering.apply(this, arguments);
			}
		},
		
		/* Rendering... */
		renderer: function(oRM, oControl) {
		},
		
		/* Changes, after rendering is done... */
		onAfterRendering: function() {
			if(Panel.prototype.onAfterRendering) {
				Panel.prototype.onAfterRendering.apply(this, arguments);
			}
		}
	});
});
  1. define: we are importing sap.m.Panel itself to extend it, sap.ui.Device to be able to do some responsive rendering. Next, I’m using VerticalLayout for the panel’s content, as a wrapper (the menu) and lastly we need a toolbar for the panel, which will have the slide button
  2. We will add our own metadata-properties to our slide panel and above is a preparation for that
  3. I added all the lifecycle method that we will need for the whole SlidePanel: all of them, except for the renderer itself, are using the standard Panel

 

1.2. Initialize the control

In lifecycle method init, we add and set important, extra properties we will need later:

// add SlidePanel object local properties:
// width that needs to be slided: set after rendering, depending on rendered object
this._layoutWidth = 0; 
// current panel status: whether it's slided away or not (used in button press handler)
this._slidedAway = this.getStartVisible() ? false : true;
// button for sliding the SlidePanel 
// -> added to SlidePanel object for easy access in later methods
this._oSlideButton = new sap.m.Button({
	press: this._onSlidePanel.bind(this) // IMPORTANT to bind to SlidePanel object
});
// position of the button in the panel's header toolbar
this._oSlideButton.addStyleClass("absoluteTopRight"); 
  1. We need a property for the width of the finished, rendered panel, which will be slid left and right
  2. We need a property for easy knowing, whether the panel is currently in view or not
  3. As mentioned many times, we need a button for the sliding. We add it here during init as an object property, for easy access in the other lifecylce methods. We even add a press event, which points to a not yet added method in this JS file (see below). It’s very important to bind the press event handler to this extended panel, in order to access this object
  4. Lastly we give the slide button a CSS class, which will position it in the header toolbar, in the upper right corner

 

1.2.1. Slide button event handler

Add following method after the lifecycle methods to your JS file (we fill it later):

		/* =========================================================== */
		/* event handlers                                              */
		/* =========================================================== */
		
		/* slide button's press event */
		_onSlidePanel: function() {
		}

1.2.2. Slide button CSS style class

In your custom CSS file, add following style class: PROJECT/webapp/css/style.css

/* POSITIONS: position panel slide button */
.absoluteTopRight {
	position: absolute;
	top: 0;
	right: 0;
	z-index: 999; /* make sure it's on top */
}

 

1.3. Prepare Control to be rendered

First, let’s add our own extra properties, needed for the SlidePanel:

metadata: {
	properties: {
		startVisible: {
			type: "boolean",
			defaultValue: false
		},
		autoWidth : {
			type: "boolean",
			defaultValue: true
		},
		toolbarTitle: {
			type: "string",
			defaultValue: ""
		},
		slideButtonType: {
			type: "string",
			defaultValue: sap.m.ButtonType.Transparent
		},
		slideButtonTooltip: {
			type: "string",
			defaultValue: "Click to slide panel in and out"
		},
		slideButtonVisIcon: {
			type: "string",
			defaultValue: "sap-icon://navigation-left-arrow"
		},
		slideButtonHidIcon: {
			type: "string",
			defaultValue: "sap-icon://navigation-right-arrow"
		},
		contrastStyle : {
			type: "boolean",
			defaultValue: true
		}
	}
},
  1. startVisible: whether the slide panel should start visible (in view, slid out) or not (out of view, slid away)
  2. autoWidth: whether the width of the panel should be determined by the control (responsive)
  3. toolbarTitle: optional text that can be added to the header toolbar, which will be displayed as a title
  4. slideButtonType: sap.m.ButtonType that should be used for the sliding button
  5. slideButtonTooltip: tooltip when hovering over the button
  6. slideButtonVisIcon: icon on the slide button, when the whole slide panel is visible/in view
  7. slideButtonHidIcon: icon on the slide button, when the whole slide panel is hidden/out of view
  8. contrastStyle: whether or not the contrast style should be used for the slide panel (for the parts that are not transparent of course)

These properties give you some freedom to style the panel and button as wished, so we are not hard-coding everything. And just a quick FYI regarding the contrastStyle property: I created my panel and I just didn’t like “bright on bright”, so to me, contrast in fact looked better (specially on mobile). That is why above, I set the default actually to true.

Next we still need to do a few things, before we render the control – lifecycle method onBeforeRendering:

/* Changes before rendering... */
onBeforeRendering: function() {
	if(Panel.prototype.onBeforeRendering) {
		Panel.prototype.onBeforeRendering.apply(this, arguments);
	}
	
	/* Force transparent style on the panel */
	this.setBackgroundDesign("Transparent");
	
	/* Determine/set width */
	if(this.getAutoWidth() || !this.getWidth()) {
		if(Device.system.tablet && Device.orientation.portrait) {
			this.setWidth("50%");
		} else if(Device.system.tablet && Device.orientation.landscape) {
			this.setWidth("40%");
		} else if(Device.system.desktop) {
			this.setWidth("25%");
		} else {
			this.setWidth("80%");
		}
	} 
	
	/* Force header toolbar 
	  (which will have the button for sliding and optional a text for the title) */
	var oTB = new Toolbar({
		design: "Solid",
		style: "Clear"
	});
	if(this.getContrastStyle()) {
		oTB.addStyleClass("sapContrast");
	}
	oTB.addContent(new sap.m.Text({ 
		text: this.getToolbarTitle(), // set toolbar title
		textAlign: "Center",
		width: "90%"
	}));
	// style the already created slide button for header toolbar
	this._oSlideButton.setType( this.getSlideButtonType() );
	this._oSlideButton.setTooltip( this.getSlideButtonTooltip() );
	this._oSlideButton.setIcon( this.getStartVisible() ? 
		this.getSlideButtonVisIcon() : this.getSlideButtonHidIcon() 
	);
	// add button to toolbar
	oTB.addContent(this._oSlideButton); 
	this.setHeaderToolbar(oTB);
},

I know it’s quite a bit of code, but don’t get discouraged, it’s rather simple. Let’s go through it:

  1. As mentioned in the Solution Design, we need the panel to be transparent at all times. No matter the theme etc. So we have to force it here. This way, even if the developer wrongly set backgroundDesign in the view (because we extend sap.m.Panel of course it’s a valid option), we will force it here into Transparent
  2. We want to handle the width and make sure it is set, so once the renderer is called, all we need to do is use this.getWidth(). For this we have our own property autoWidth as well as the UI5 width property. First we check, if our own property is set to true; in that case we would overwrite, if something was given in the width property on the XML view. Secondly we also check if the width property is empty; in that case we act in the same way, as if autoWidth was set to true. Meaning this way we ensure, width has always a value, but giving the developer the chance to choose a width, if that is needed and autoWidth, does not give the desired result.
    The code that follows checks the Device and suggests a simple percentage width, depending on the current Device’s width
  3. Next, also mentioned several times, we know we need a toolbar so we create it here, and set it as the standard sap.m.Panel HeaderToolbar. Meaning once again, if on the XML view the developer would use HeaderToolbar, we would in fact overwrite it here, because we need it!
    We also add contrast style here, if needed and we add the toolbar title here and make sure it is centered (if there is no title given, there won’t be any text).
  4. Next we reuse the already created slide button property and now we style it, with all the properties we created for this (note: styling in the init method would not work as default values will be applied later!). Finally we add the button to the toolbar and set the panels HeaderToolbar

 

To be continued in Part 2…

We’ve done quite a bit: however, not yet a useable, finished control but I feel like this blog post is getting really big so I’m splitting here. Please check Part 2 to finish the Slide Panel control.

EDIT:ย Part 2 is live! Click on the link and check it out!

3 Comments
You must be Logged on to comment or reply to a post.