Technical Articles
Creating a Sliding Panel control with SAPUI5 – Part 2
Hello,
This is part 2 of my Slide Panel blog posts. Please check Part 1 if you haven’t yet 🙂
1.5. Rendering the Control
Now we are ready to render our Slide Panel, using the SAPUI5 RenderManager (check the link for the API reference, if you are unfamiliar with this).
/* Rendering... */
renderer: function(oRM, oControl) {
// start slide panel div with id
oRM.openStart("div", oControl.getId());
// position and style slide panel
oRM.style("position", "absolute");
oRM.style("top", "0");
oRM.style("left", "0");
oRM.style("z-index", "999");
if(oControl.getStartVisible()) {
oRM.style("visibility","visible");
} else {
oRM.style("visibility","hidden");
}
// set width
oRM.style("width", oControl.getWidth());
// PANEL CONTENT ----------------------------
oRM.openEnd();
// toolbars
var oTB = oControl.getHeaderToolbar();
if(!oTB) {
// N/A
} else {
oRM.renderControl(oTB);
}
oTB = null;
oTB = oControl.getInfoToolbar();
if(!oTB) {
// N/A
} else {
oRM.renderControl(oTB);
}
// content - add a wrapper around the content
var oLayout = new VerticalLayout({
width: "100%"
});
if(oControl.getContrastStyle()) {
oLayout.addStyleClass("sapContrast");
oLayout.addStyleClass("layoutContrastBG");
} else {
oLayout.addStyleClass("sapMPageBgStandard");
}
oLayout.addStyleClass("smallPadding");
// add the content from SlidePanel from the view, into the wrapper
oControl.getContent().forEach(function(oCtrl) {
oLayout.addContent(oCtrl);
});
// render wrapper
oRM.renderControl(oLayout);
// close slide panel div
oRM.close("div");
},
Now, whilst in the renderer method, it is important to note that we have the RenderManager (oRM in my code) and the Slide Panel control (oControl) given as attributes. Meaning this won’t in fact access the Slide Panel directly, instead use oControl!
Rendering is a little bit it’s own little thing and if you are unfamiliar with it, please check the link above under RenderManager. I won’t go into detail here.
- Start the slide panel as a simple DIV, styling it in the top-left-corner of the page (or content it’s placed in) – that’s the starting position – we will slide it away later (after rendering in fact) if our own property startVisible (from Part 1) is set to false. We also make it already now either visible or not, depending on startVisible (otherwise the user would see the panel being slid out of view, while loading). And we give the DIV the corresponding width property, which we already set in Part 1
- The panel’s content has two main parts: the toolbar and the content
- We simply retrieve the HeaderToolbar control through the getter, and render it as the control would through SAPUI5 standard, by using the RenderManager’s renderControl method. Then we do the same for the InfoToolbar, simply because that is a valid option for sap.m.Panel, however I have not actually tested it!
- To add the content (which is free for the developer to fill on the XML view) we first add a wrapper around, so we can set the appropriate width later on, once the rendering is done (100% of the transparent panel, minus width & margin of the sliding button). But we start by giving it 100%, so we can later on simply subtract.
Another reason for using a wrapper, is that now, we can apply the corresponding styling: either contrast or standard background. Lastly I gave the VerticalLayout some padding: I had tried the responsive padding classes provided by UI5, but none seemed ok to me: on desktop there was way too much padding and on mobile, none at all. So I opted for a middle way out (padding: 0.5 em in the CSS file for the CSS class) - Now to add the actual content, we use the getter method of our panel control and then we in fact add those controls, into the VerticalLayout’s content instead (so we never render it as the panel’s “direct content”)
- Now the VerticalLayout is done, and once again, UI5 will take care of rendering all of those controls for us, by just using renderControl for the whole VerticalLayout
- And we close the DIV
That’s it, our control is rendered!
BUT – we still need to do some fixes for our nice feature: to have only the button visible, should be SlidePanel be out of view – and this we do after the rendering:
/* Changes, after rendering is done... */
onAfterRendering: function() {
if(Panel.prototype.onAfterRendering) {
Panel.prototype.onAfterRendering.apply(this, arguments);
}
// set VerticalLayout width:
// 1) subtract slide button width & margin (we don't know this until after rendering)
// 2) consider layout padding
var oLayout = this.$().find(".sapUiVlt");
if(oLayout.length === 1) {
var oBtn = this._oSlideButton.$(), // get jQuery object of button
padding = parseInt( oLayout.css("padding-left").slice(0, -2), 10)
+ parseInt( oLayout.css("padding-right").slice(0, -2), 10),
bMargin = parseInt( oBtn.css("margin-left").slice(0, -2), 10)
+ parseInt( oBtn.css("margin-right").slice(0, -2), 10);
oLayout.width(oLayout.width() - oBtn.width() - bMargin - padding);
// set sliding width (including padding)
this._layoutWidth = oLayout.width() + padding;
}
// check if the panel needs to be slided away at start
if(!this.getStartVisible()) {
var oSP = this.$(); // to use in animate callback
// slide away
this.$().animate( { left:"-" + this._layoutWidth }, 100, function() {
oSP.css("visibility", "visible"); // make it visible, now that it's slided away
});
}
},
- First we use jQuery through this.$() and we use the find method, in order to get our VerticalLayout inside our panel control.
- We make sure we found the control and next, we get our slide button and we want this time the jQuery object of it. Now using the layout and the button, we calculate the padding of the layout, the button width and the button margin. Then we set the layout’s new width, with the jQuery width method: using the current width (which is 100% if your remember from above), subtracting button width, subtracting button margin and subtracting the layout’s padding.
- Next we set the global property we added in Part 1, which will have the exact width, that will be slid left and right: the newly just calculated width of the layout, plus its padding
- Lastly, we need to check if our SlidePanel starts of visible or slid away: if slid away, we now need to do that, because after rendering, it is currently in view:
- we get the whole panel as jQuery object, again by using this.$()
- we use jQuery animate method and slide the panel left and out of view, for the width that we just calculated, leaving only the button visible
- REMEMBER: we actually made the whole contron invisible in the rendering step above, if we want to start slid away. This means however everything is invisible, including the slide button. We had to do this, as explained, because the user would otherwise see, this “sliding out”. But now (on animate callback method) we need to make the panel visible: jQuery css method.
That’s it. Our control is done and rendered beautifully. Just a few steps remain.
1.6. The Sliding Method
In part 1 we already added the press event handler, for the sliding button. But we had no code in it just yet, but basically we will do something very similar, to just above, during after rendering:
/* slide button's press event */
_onSlidePanel: function() {
var that = this; // to use in animate callback
// slide the panel left/right using jQuery animate
if(that._slidedAway) {
that.$().animate( { left:"0" }, 350, function() {
that._oSlideButton.setIcon(that.getSlideButtonVisIcon());
that._slidedAway = false;
});
} else {
that.$().animate( { left:"-" + that._layoutWidth }, 350, function() {
that._oSlideButton.setIcon(that.getSlideButtonHidIcon());
that._slidedAway = true;
});
}
}
- Set local variable to be used in jQuery animate callback
- We check the current state of the SlidePanel: currently slid out or not
- If currently slid away: we animate the panel into view by sliding it back to its starting position: left 0. When the sliding is done, we set the appropriate button and set the new current state property
- If currently in view, we animate the panel out of view by sliding it to the left, with the width we had calculated after rendering. When the sliding is done, we set the appropriate button and set the new current state property
That’s it, the control is ready to be used. See following chapters how to use it.
2. Using the Control
A small disclaimer here: since I’m using simple CSS absolute positioning for this use case, adding this SlidePanel into a header content or any kind of toolbar, will not work. The panel would probably not fit and the overflow would not be visible or mess up in same way.
So please note: it is recommended to use this control in a content attribute of something big enough.
2.1. Adding custom control library to view
We need to add our own library to the view to access our own SlidePanel control:
<core:FragmentDefinition
xmlns="sap.m"
xmlns:core="sap.ui.core"
xmlns:layout="sap.ui.layout"
xmlns:ext="NAMESPACE.controls">
2.2. Adding control to view
<content>
<ext:SlidePanel
id="slidemenu"
toolbarTitle="{i18n>menu}">
<Input
id="inputUser"
placeholder="{i18n>userPH}"
value="{user>/username}" />
<CheckBox
text="{i18n>showCalCheck}"
selected="{user>/showCalories}" />
<CheckBox
text="{i18n>showDVCheck}"
selected="{user>/showDV}" />
<RadioButtonGroup
class="noFocusBorder"
select="onSortChange">
<buttons>
<RadioButton
text="{i18n>sortScore}"
selected="{user>/sort/sortScore}" />
<RadioButton
text="{i18n>sortName}"
selected="{user>/sort/sortName}" />
</buttons>
</RadioButtonGroup>
</ext:SlidePanel>
...
</content>
With ext:SlidePanel (the name of the js file) we can now create our control. In above example I’m basically using the default values and just giving a title (but all properties can be used here of course).
Above as an example I added couple of different controls, to show that any control inside the panel, will be rendered as usual.
3. The Result
And here some screenshots, how my SlidePanel looks like:
Here only the button is visible (the dark one on the left of “Good Morning”)
Once the button is clicked, the whole panel will slide (nice animation) and will be visible and the arrow on the button swaps:
Great post.Thank you.Is there a way to pull the slider manually?
Thanks! Glad you liked it.
Personally I see a few issues with having the user being able to slide left/right on his/her own. You would need to add quite some checks and restraints to make sure it can't be pulled out of place and you'd need to keep track of its exact position.
Nevertheless, I really like the general idea! If I have some time I will look into it and maybe post a part 3, with a more engaging touch/pull sliding 🙂