Here's a ui5tip to show how you can change the look and feel of the
sap.m.TabContainer
with a minimal amount of custom CSS. If you want to try this for yourself, be sure to
check out the sample application from github.
The sap.m.TabContainer
The
sap.m.TabContainer
provides a simple, no-nonsense widget to build a tabbed user interface (check out
the samples). Tabs can be added via
the items aggregation, which should contain a collection of
sap.m.TabContainerItem
's.
While this control generally suits my needs, it has one feature I find problematic: each tabs always has a close button, which appears as a little 'X' icon in the right side of the tab. If the user clicks it, it will actually 'close' the tab, that is: the respective
sap.m.TabContainerItem
will be removed from the TabContainer.
See the screenshot below to see what the default looks like (close buttons highligthed in red):
Suppressing the close action
The openui5 samples show how you can suppress that behavior: you can write an event handler for
the itemClose
event, and then call
the preventDefault()
method on the event:
(In the view xml:)
<m:TabContainer itemClose="onTabContainerItemClose">
<m:items>
<m:TabContainerItem>
...
</m:TabContainerItem>
<m:TabContainerItem>
...
</m:TabContainerItem>
</m:items>
</m:TabContainer>
(In the controller javascript:)
onTabContainerItemClose: function(event){
event.preventDefault();
}
Obviously, it would be strange if we'd always prevent the tab from being closed: Suppressing the default action of closing the tab only makes sense in a context where the user is supposed to be able to close the tab at all, and in such a case this could be used to pop up a dialog to let the user choose if they really meant to close the tab or want to keep it open.
But the use case I frequently encounter is that the tab should not be closeable in the first place. While suppressing the close action would ensure the tab is never closed, it would confuse and anger the user, as the close button itself would still be there, inviting users to perform an action that can never be fulfilled.
Using the other Tab widget
One might suggest to use the
sap.m.IconTabBar
widget instead of the
sap.m.TabContainer
. The
sap.m.IconTabBar
takes
sap.m.IconTabFilter
's in its items collection, and these do not have a close button.
Now, in some cases the
sap.m.IconTabBar
/
sap.m.IconTabFilter
may suit your needs and then you're fine. However I find that it has a number of other drawbacks (which I won't get into here).
Besides, the
sap.m.IconTabBar
introduces a similar problem, but the other way around: whereas we cannot get rid of the close button in the
sap.m.TabContainer
, we cannot ever have a close button in the
sap.m.IconTabBar
. What we really want, is a property or something like that, which will let us control whether the tab will have a close button or not.
CSS to the rescue
In the previous section we argued that we'd really like to be able to control for each individual tab whether they have a close action at all, for example, by setting a property.
To add a property one would normally have to extend a ui5 control, and attach some code so that the property setting can somehow influence the behavior of the control - in this case, control whether or not the close button will be displayed. While this is probably possible (I haven't tried it for this case), it does seem like an extraordinary measure for such a humble request.
I found that a similar effect can be achieved by
applying some custom CSS in combination with standard ui5 features. That's what this entire sample is about.
With this tip you can:
- hide all close buttons for an entire
sap.m.TabContainer
- hide the close button on an individual
sap.m.TabContainerItem
- show the close button on an individual
sap.m.TabContainerItem
in case the close buttons are hidden by default on the sap.m.TabContainer
All this functionality requires the inclusion of some css. In the sample this is all isolated in a single
ui5-customization.css
file, which is included into the application by
declaring it the manifest.json.
Hiding all close buttons
To hide all the close buttons for all
sap.m.TabContainerItem
in the items collection of a particular
sap.m.TabContainer
, simply add the
noCloseButtons
style class:
<m:TabContainer
class="noCloseButtons"
>
<m:items>
<!-- note: close button will be hidden by default for each m:TabContainerItem -->
...
</m:items>
</m:TabContainer>
This works because the
class
property in the ui5 xml view is rendered to the html dom directly. So whatever html elements that ui5 creates to implement the TabControl widget will then be selectable with a class selector in css, and this is how we can relatively simply influence the look of our TabContainer through css.
In our
ui5-customization.css
file, this is how we use
noCloseButtons
class to hide the buttons:
div.sapMTabContainer.noCloseButtons > .sapMTabStripContainer > .sapMTabStrip > .sapMTSTabsContainer > .sapMTSTabs > .sapMTabStripItem > .sapMTSItemCloseBtnCnt {
visibility: hidden;
}
(Note the initial selector,
div.sapMTabContainer.
noCloseButtons
and the chain of
>
child selectors which target the actual bit of html that is used to render the close button, which is simply hidden by setting the css
visibility
property to
hidden
.)
In the sample application, you can see this behavior in action in the
App.view.xml. This contains the code for the outermost tabcontainer. A screenshot is shown below and as you can see both tabs ("Hide individually" and "Show individually") do not have a close button:
Hiding an individual close button
If we can add a custom css class to
sap.m.TabContainer
to hide all close buttons, then surely it should be possible to follow the same approach for an individual
sap.m.TabContainerItem
, right? Yes, it should,
but sadly, we cannot. (The reason is that
sap.m.TabContainer
is a subclass of
sap.m.Control
, which provides a
addStyleClass()
method, whereas
sap.m.TabContainerItem
is a subclass of
sap.ui.core.Element
, which does not have such a method)
Now, let's take a step back and think about how we used the css style class on the
sap.m.TabContainer
to hide all the close buttons. By setting the custom css style class on the
sap.m.TabContainer
, the html dom was changed to include the custom class, and we could then use that in a css selector.
So even if we cannot apply a css style class to a
sap.m.TabContainerItem
, might there be another way that would allow us to influence how ui5 writes the html dom so we may write a css selector in our custom css? It turns out that such a feature exists in the shape of a feature called
ui5 custom data.
The
custom data aggregation is provided by
sap.ui.core.Element
and thus available to its subclasses, including
sap.m.TabContainerItem
. A
custom data item is an arbitrary key/value pair, and by setting its
writeToDom
property, ui5 will render it to the html dom as a
html data attribute.
To see what it looks like in our sample, take a look at
TabContainerItemWithHiddenCloseButton.fragent.xml
, which uses it to hide the close button in the second
sap.m.TabContainerItem
, in an otherwise normal
sap.m.TabContainer
:
<m:TabContainerItem
id="item2"
name="No Close Button"
>
<m:customData>
<core:CustomData writeToDom="true" key="noCloseButton" value="true"/>
</m:customData>
...
</m:TabContainerItem>
Because the
CusomtData
's
writeToDom
property is set to
true
, ui5 will generate a html data attribute to the html dom that looks something like this:
data-noclosebutton='true'
And in our
ui5-customization.css
file, the following rule is intended to pick that up and hide the close button:
div.sapMTabContainer > .sapMTabStripContainer > .sapMTabStrip > .sapMTSTabsContainer > .sapMTSTabs > .sapMTabStripItem[data-noclosebutton='true'] > .sapMTSItemCloseBtnCnt {
visibility: hidden;
}
As you can see it is very similar to the rule we used to hide all close buttons on any
sap.m.TabContainer
having the
noCloseButtons
class, except now the class is missing and instead we use a css predicate selector based on the data attribute:
.sapMTabStripItem[data-noclosebutton='true']
The screenshot below shows what it looks like in the app. Note that the tab named "Default" has the close button as usual, but the one named "No Close Button - the one with the custom data attribute - does not show a close button:
Showing an individual close button
The final hack in this sample combines the style class and the custom data attribute. CSS allows us to write a selector that takes both the presence of the css style class as well as the presence of a html data attribute into account. We can put this to good use if we want to have a
sap.m.TabContainer
that hides all close buttons by default, but undo the hiding of the close button of specific
sap.m.TabContainerItem
's, based on the value of a data attribute (which is in turn controlled by the ui5 Custom Data feature).
You an see this in action in the
TabContainerItemWithHiddenCloseButtons.fragment.xml
file of the example:
<m:TabContainer
class="noCloseButtons"
>
<m:items>
<m:TabContainerItem name="Default">
...
</m:TabContainerItem>
<m:TabContainerItem name="Show Close Button">
<m:customData>
<core:CustomData key="noCloseButton" value="false" writeToDom="true" />
</m:customData>
..
</m:TabContainerItem>
</m:items>
</m:TabContainer>
Again the second
sap.m.TabContainerItem
has a
CustomData
item with the key
noCloseButton
, but now the value is
true
so as to override the effect of the
noCloseButtons
style class applied to the
sap.m.TabContainer
.
In our our
ui5-customization.css
file, the following rule is intended to pick that up and show the close button:
div.sapMTabContainer > .sapMTabStripContainer > .sapMTabStrip > .sapMTSTabsContainer > .sapMTSTabs > .sapMTabStripItem[data-noclosebutton='false'] > .sapMTSItemCloseBtnCnt {
visibility: visible;
}
The screenshot below shows what it looks like when you run the sample application:
Finally
Did you like this tip? Do you have a better tip? Feel free to post a comment and share your approach to the same or similar problem.
Want more tips? Find other posts with the
ui5tips tag!
No comments:
Post a Comment