Page Is Blank Without Throwing Any Errors - sapui5

I am trying to display few tiles in a tile container which fetches data from a dummy JSON file. I have coded exactly shown in this sample. But my page appears empty. Also it doesn't show any errors in the console. Below are the snippets of my code.
View1.controller.js
sap.ui.define([
"sap/ui/core/mvc/Controller"
], function(Controller) {
"use strict";
return Controller.extend("AdminMovie.controller.View1", {
});
});
View1.view.xml
<mvc:View
displayBlock="true"
controllerName="AdminMovie.controller.View1"
xmlns:mvc="sap.ui.core.mvc"
xmlns="sap.m"
>
<Page showHeader="false" enableScrolling="false">
<mvc:XMLView viewName="AdminMovie.view.TileContainer"/>
<footer>
<OverflowToolbar id="otbFooter">
<ToolbarSpacer/>
<Button type="Accept" text="Add New Movie"/>
</OverflowToolbar>
</footer>
</Page>
</mvc:View>
TileContailner.view.xml
<mvc:View
xmlns:core="sap.ui.core"
xmlns:mvc="sap.ui.core.mvc"
xmlns="sap.m"
controllerName="AdminMovie.controller.TileContainer"
>
<App>
<pages>
<Page
showHeader="false"
enableScrolling="false"
title="Stark"
>
<TileContainer id="container"
tileDelete="handleTileDelete"
tiles="{/MovieCollection}"
>
<HBox>
<StandardTile
icon="{icon}"
type="{type}"
number="{number}"
numberUnit="{numberUnit}"
title="{title}"
info="{info}"
infoState="{infoState}"
/>
</HBox>
</TileContainer>
<OverflowToolbar>
<Toolbar>
<ToolbarSpacer/>
<Button
text="Edit"
press=".handleEditPress"
/>
<ToolbarSpacer/>
</Toolbar>
</OverflowToolbar>
</Page>
</pages>
</App>
</mvc:View>
TileContainer.js
sap.ui.define([
"jquery.sap.global",
"sap/ui/core/mvc/Controller",
"sap/ui/model/json/JSONModel"
], function(jQuery, Controller, JSONModel) {
"use strict";
return Controller.extend("AdminMovie.controller.TileContainer", {
onInit: function(evt) {
// set mock model
var sPath = jQuery.sap.getModulePath("AdminMovie", "/MovieCollection.json");
var oModel = new JSONModel(sPath);
this.getView().setModel(oModel);
},
handleEditPress: function(evt) {
var oTileContainer = this.byId("container");
var newValue = !oTileContainer.getEditable();
oTileContainer.setEditable(newValue);
evt.getSource().setText(newValue ? "Done" : "Edit");
},
handleTileDelete: function(evt) {
var tile = evt.getParameter("tile");
evt.getSource().removeTile(tile);
}
});
});

Cause
The root view is missing a root control or the height of the parent HTML elements is not set to 100%. The child elements cannot be rendered in full size.
Resolution
For Standalone or Top-Level Applications
Add sap.m.App (or sap.m.SplitApp in case of a master-detail layout) once in the entire application project to your root view:
<!-- Root view (typically "App.view.xml") -->
<mvc:View controllerName="..."
xmlns:mvc="sap.ui.core.mvc"
xmlns="sap.m"
displayBlock="true"
>
<App id="topLevelApp"> <!-- Not in any other views! -->
<pages>
<!-- ... -->
</pages>
</App>
</mvc:View>
Root controls such as sap.m.App, sap.m.SplitApp, and sap.m.Shell write:
a bunch of properties into the header of the HTML document e.g. the viewport meta tag via sap/ui/util/Mobile.init.
height: 100% to all its parent elements by default (unless isTopLevel is disabled). src
The reason why the linked sample is working, is that the control sap.m.App was already added in index.html. The samples shown in the Demo Kit, however, often miss index.html in the code page to be shown which can be confusing.
For Nested or Embedded Applications
If you're developing an app that is to be rendered within an an existing app, keep in mind that the top-level app might come already with one root control (sap.m.App with isTopLevel enabled or sap.m.SplitApp) in its root view. I.e. in this case:
Add height="100%" to the View node of the root view definition, and
Either use <App isTopLevel="false"> or replace the <App> with <NavContainer>.
<!-- Root view (typically "App.view.xml") -->
<mvc:View controllerName="..."
xmlns:mvc="sap.ui.core.mvc"
xmlns="sap.m"
displayBlock="true"
height="100%"
> <!-- ↑ Set height="100%" -->
<App isTopLevel="false"> <!-- or:
<NavContainer> if the UI5 version is below 1.91 -->
<pages>
<!-- ... -->
</pages> <!--
</NavContainer> -->
</App>
</mvc:View>
Otherwise, not using the isTopLevel="false" will cause the footer area of the embedded app to be pushed out of the viewport. This is a common issue e.g. for the views that are intended to extend the standard SAP Fiori app "My Inbox" since the app already contains one root control as a top-level UI element. Refer to SAP KBA #3218822.

⚠️ For other readers: if you're not using a TileContainer, see my previous answer for general solutions → https://stackoverflow.com/a/50951902/5846045
Causes for empty TileContainer
Unexpected child control
Besides the missing root control, the <TileContainer> in your code contains a list of HBoxes.
<TileContainer>
<HBox> <!-- ❌ Wrong aggregation child! -->
<StandardTile />
The default aggregation of TileContainer is, however, a control that is derived from sap.m.Tile.
Hence, you should be getting the following error message in the browser console:
Uncaught Error: "Element sap.m.HBox#__hbox1" is not valid for aggregation "tiles" of Element sap.m.TileContainer#__xmlview1--container
Please, remove the <HBox> as the binding template:
<TileContainer>
<StandardTile /> <!-- ✔️ -->
TileContainer contains only one Tile
There was a bug (issue #1813) in the TileContainer which failed making the aggregation visible in Chrome (works in Firefox) if there was only a single Tile. The fix is delivered with OpenUI5 version 1.56+, 1.54.2+, 1.52.8+, 1.50.9+, 1.48.19+, and 1.46.2+.

Related

How to get notified when a section of sap.uxap.ObjectPageLayout changes in SAPUI5 1.38?

I am working with a SAPUI5 version 1.38 and use in an application the control sap.uxap.ObjectPageSection within sap.uxap.ObjectPageLayout. At a certain section, when you click on it, an event should happen.
I haven't found a solution for this yet unfortunately. See:
I have already tried with attachBrowserEvent and addEventDelegate. But unfortunately, this does not work for this control.
Does anyone have any other ideas how to solve this problem? A workaround?
I would be very grateful for any help.
View1.controller.js
sap.ui.define([
"sap/ui/core/mvc/Controller"
], function (Controller) {
"use strict";
return Controller.extend("test123.test123.controller.View1", {
onInit: function () {
},
onAfterRendering: function () {
var showMessage = function () {
sap.m.MessageToast.show("text");
return false;
};
this.byId("section2").attachBrowserEvent("click", showMessage);
this.byId("section2").addEventDelegate({
onclick: function () {
sap.m.MessageToast.show("text");
}
});
}
});
});
View1.view.xml
<mvc:View controllerName="test123.test123.controller.View1" xmlns:mvc="sap.ui.core.mvc" xmlns:html="http://www.w3.org/1999/xhtml"
displayBlock="true" xmlns="sap.m" xmlns:core="sap.ui.core" xmlns:uxap="sap.uxap">
<App id="idAppControl">
<pages>
<Page title="{i18n>title}">
<content>
<uxap:ObjectPageLayout id="ObjectPageLayoutId"
useIconTabBar="true"
alwaysShowContentHeader="true">
<!-- SECTION 1 -->
<uxap:sections>
<uxap:ObjectPageSection titleUppercase="false" id="section1" title="Section 1">
<uxap:subSections>
<uxap:ObjectPageSubSection>
<uxap:blocks>
<Label text="Content 1" />
</uxap:blocks>
</uxap:ObjectPageSubSection>
</uxap:subSections>
</uxap:ObjectPageSection>
</uxap:sections>
<!-- SECTION 2 -->
<uxap:sections >
<uxap:ObjectPageSection titleUppercase="false" id="section2" title="Section 2">
<uxap:subSections>
<uxap:ObjectPageSubSection>
<uxap:blocks>
<Label text="Content 2" />
</uxap:blocks>
</uxap:ObjectPageSubSection>
</uxap:subSections>
</uxap:ObjectPageSection>
</uxap:sections>
</uxap:ObjectPageLayout>
</content>
</Page>
</pages>
</App>
</mvc:View>
Check out the documentation of 1.38. There is an event called tabSelect. The event is comparable to the event navigate in 1.40.[1]
You can use it as long as the property useIconTabBar equals true:
<uxap:ObjectPageLayout tabSelect=".onTabSelect" useIconTabBar="true">
[1] ⚠️ BREAKING CHANGE: with commit eb75945 (merged with the 1.40 release), the event tabSelect gets removed in favor of navigate! If the application runs with 1.40 or higher in the future, tabSelect should be renamed to navigate also in your code. But please review the differences between the two events beforehand.

Executing a function based on a click event on an html element in SAPUI5

Hi I'm trying to get a function defined in my controller to execute when I press on a specific area of a picture. I'm using an html map to define the areas of my picture (code below)
<mvc:View id="Main" controllerName="Demo.picture.controller.Main" xmlns:html="http://www.w3.org/1999/xhtml" xmlns:mvc="sap.ui.core.mvc"
displayBlock="true" xmlns="sap.m" xmlns:core="sap.ui.core">
<App id="idAppControl">
<pages>
<Page title="{i18n>title}">
<content>
<Image src = "Some image source" alt="Image" useMap="Map"/>
</content>
</Page>
</pages>
</App>
<html:map name="Map" id="Map">
<html:area id= "area_1" shape="rect" coords= "0,0,240,215" alt="Chart" onclick="???"/>
<html:area id = "area_2" shape="rect" coords="240,0,480,215" alt="Start" onclick="???"/>
</html:map> </mvc:View>
Not sure if using onclick is the right way of doing this.
Controller code:
sap.ui.define([
"sap/ui/core/mvc/Controller"], function (Controller) {
"use strict";
return Controller.extend("Demo.picture.controller.Main", {
SomeMethodFoo: function () {
"method to be executed when area_1 is pressed"
},
SomeMethodGoo: function () {
"method to be executed when area_2 is pressed"
}
});});
Is it possible to attach an eventHandler for a onclick event to these areas? Or is there some other way to do this?
I guess that it would be something like this:
onclick="sap.ui.getCore().byId('__xmlview0').getController().SomeMethodFoo()"
but your need to manage yourself to know your view ID, which won't be always '__xmlview0'

Flexible Column Layout Navigation (SAPUI5)

I've created an app with SAPUI5 that makes use of a Flexible Column Layout.
I'm trying to navigate between pages, but I'm having difficulty.
Here is the main xml view:
<mvc:View id="Main" controllerName="SAP.demo.controller.Main" xmlns:html="http://www.w3.org/1999/xhtml" xmlns:mvc="sap.ui.core.mvc"
displayBlock="true" xmlns="sap.f" xmlns:core="sap.ui.core" xmlns:m="sap.m">
<m:App id="idAppControl">
<m:pages>
<m:Page title="{i18n>title}">
<m:content>
<FlexibleColumnLayout id="fcl" initialMidColumnPage="start" layout="TwoColumnsBeginExpanded">
<beginColumnPages>
<m:Page title = "Master">
<m:content>
<Button text="Chart Button" press="displayChart"/>
</m:content>
</m:Page>
</beginColumnPages>
<midColumnPages>
<m:Page id="start" title = "Detail">
<m:content>
<core:Fragment fragmentName="SAP.demo.view.Start" type="XML"/>
</m:content>
</m:Page>
<m:Page id="charts" title="Charts">
<m:content>
<core:Fragment fragmentName="SAP.demo.view.Charts" type="XML"/>
</m:content>
</m:Page>
</midColumnPages>
</FlexibleColumnLayout>
</m:content>
</m:Page>
</m:pages>
</m:App>
I want to navigate from the start page to the charts page. The controller is as follows:
sap.ui.define([
"sap/ui/core/mvc/Controller"], function (Controller) {
"use strict";
return Controller.extend("SAP.demo.controller.Main", {
displayChart:function(oEvent){
this.byId("fcl").toMidColumnPage("charts");
}
});});
Can someone please advise as to what I'm doing wrong, because it stays on the start page even after I press the button.
This is because the real ID of the view is not "charts" but "__xmlview0--charts".
Be careful with the ID always and use the API method byId('theIDHere').
In your case use one of the following options:
displayChart:function(oEvent){
this.getView().byId("fcl").toMidColumnPage(this.getView().byId("charts"));
}
Or
displayChart:function(oEvent){
this.getView().byId("fcl").toMidColumnPage(this.getView().byId("charts").getId());
}
And also add the correct XML namespace to your button
<m:Button text="Chart Button" press="displayChart"/>
Managed to fix it with the following code:
displayChart: function () {
this.byId("fcl").toMidColumnPage(this.createId("charts"));
},

Strange sap.m.TextArea behavior inside a sap.m.List

I'm using sap.m.TextArea control inside sap.m.List where the values of each TextArea were mapped to a JSONModel. Here is the xml-view code:
<List id="otherPicList" growing="true" items="{ path : 'newRequest>/OtherPic' }" >
<items>
<CustomListItem type="Inactive">
<Image id="otherPic" src="{newRequest>pic}" width="90px" height="60px" />
<VBox>
<TextArea value="{newRequest>text}"/>
</VBox>
</CustomListItem>
</items>
</List>
When I start to type inside the sap.m.TextArea it freezes after the first character. I can only type one more character if I click outside the control and inside again. I think this bug happen because of the data binding inside a sap.m.List control.
If I set the value property without the model binding it works just fine. Is this a known bug or am I using wrong the control?
Data binding and growing list are not smart enough:
After the value of text area is changed and updated on the input event (after the key stroke), data binding is updated with the following diff:
1 item is deleted
2 item is updated
Growing List listens to data model change and does exactly the same: it deletes the changed item and creates it again.
When the text area inside of list item is deleted from DOM, focus is lost. The new text area does not get focus and any subsequent keystroke goes to nowhere.
I will report this issue to development.
It is working for my code snippet. Please run and check. Maybe there is something else causing your issue.
<script src="https://openui5.hana.ondemand.com/resources/sap-ui-core.js" id="sap-ui-bootstrap" data-sap-ui-theme="sap_bluecrystal" data-sap-ui-libs="sap.m,sap.ui.commons"></script>
<!-- define an XMLView - normally done in a separate file -->
<script id="view1" type="sapui5/xmlview">
<mvc:View xmlns:core="sap.ui.core" xmlns:layout="sap.ui.commons.layout" xmlns:mvc="sap.ui.core.mvc" xmlns="sap.m"
controllerName="my.own.controller" xmlns:html="http://www.w3.org/1999/xhtml">
<List id="otherPicList" items="{/test}" >
<items>
<CustomListItem type="Inactive">
<Image id="otherPic" src="{pic}" width="90px" height="60px" />
<VBox>
<TextArea value="{text}"/>
</VBox>
</CustomListItem>
</items>
</List>
</mvc:View>
</script>
<script>
sap.ui.controller("my.own.controller", {
onInit:function() {
var data = {test:[{text:"123",pic:"https://www.gravatar.com/avatar/e25560c87abbbb90143653d98c9924dc?s=128&d=identicon&r=PG"},{text:"456",pic:"https://www.gravatar.com/avatar/e25560c87abbbb90143653d98c9924dc?s=128&d=identicon&r=PG"},{text:"789",pic:"https://www.gravatar.com/avatar/e25560c87abbbb90143653d98c9924dc?s=128&d=identicon&r=PG"},{text:"101112",pic:"https://www.gravatar.com/avatar/e25560c87abbbb90143653d98c9924dc?s=128&d=identicon&r=PG"}]};
var oModel = new sap.ui.model.json.JSONModel();
oModel.setData(data);
this.getView().setModel(oModel);
}
});
var myView = sap.ui.xmlview("myView", {viewContent:jQuery('#view1').html()}); //
myView.placeAt('content');
</script>
<body class='sapUiBody'>
<div id='content'></div>
</body>
I found out the problem, check out your code snippet including the property growing="true" inside the sap.m.List control. This property cause the strange behavior I was talking about.
<script src="https://openui5.hana.ondemand.com/resources/sap-ui-core.js" id="sap-ui-bootstrap" data-sap-ui-theme="sap_bluecrystal" data-sap-ui-libs="sap.m,sap.ui.commons"></script>
<!-- define an XMLView - normally done in a separate file -->
<script id="view1" type="sapui5/xmlview">
<mvc:View xmlns:core="sap.ui.core" xmlns:layout="sap.ui.commons.layout" xmlns:mvc="sap.ui.core.mvc" xmlns="sap.m"
controllerName="my.own.controller" xmlns:html="http://www.w3.org/1999/xhtml">
<List id="otherPicList" growing="true" items="{/test}" >
<items>
<CustomListItem type="Inactive">
<Image id="otherPic" src="{pic}" width="90px" height="60px" />
<VBox>
<TextArea value="{text}"/>
</VBox>
</CustomListItem>
</items>
</List>
</mvc:View>
</script>
<script>
sap.ui.controller("my.own.controller", {
onInit:function() {
var data = {test:[{text:"123",pic:"https://www.gravatar.com/avatar/e25560c87abbbb90143653d98c9924dc?s=128&d=identicon&r=PG"},{text:"456",pic:"https://www.gravatar.com/avatar/e25560c87abbbb90143653d98c9924dc?s=128&d=identicon&r=PG"},{text:"789",pic:"https://www.gravatar.com/avatar/e25560c87abbbb90143653d98c9924dc?s=128&d=identicon&r=PG"},{text:"101112",pic:"https://www.gravatar.com/avatar/e25560c87abbbb90143653d98c9924dc?s=128&d=identicon&r=PG"}]};
var oModel = new sap.ui.model.json.JSONModel();
oModel.setData(data);
this.getView().setModel(oModel);
}
});
var myView = sap.ui.xmlview("myView", {viewContent:jQuery('#view1').html()}); //
myView.placeAt('content');
</script>
<body class='sapUiBody'>
<div id='content'></div>
</body>

How to dynamically load an XML fragment in XML view?

Suppose I have the following XML view:
<mvc:View xmlns:mvc="sap.ui.core.mvc" ...>
<Page>
<content>
<l:VerticalLayout>
<l:content>
<core:Fragment fragmentName="my.static.Fragment" type="XML" />
</l:content>
</l:VerticalLayout>
</content>
</Page>
</mvc:View>
The fragment my.Fragment is statically loaded. However, I now want to dynamically change the to-be-loaded fragment (ideally using data binding the fragmentName property, but any other means should be ok as well), ie. something like this:
<mvc:View xmlns:core="sap.ui.core.mvc" ...>
<Page>
<content>
<l:VerticalLayout>
<l:content>
<core:Fragment fragmentName="{/myDynamicFragment}" type="XML" />
</l:content>
</l:VerticalLayout>
</content>
</Page>
</mvc:View>
However, the latter does not work, and the Fragment definitions don't allow for data binding... I might have missed something, but how should I dynamically change the Fragment in my XML view based on a parameter/model property/etc?
For now, I have resorted to a custom control instead of directly using a fragment in my view, and have that control do the dispatching to the appropriate Fragment, but I feel there should be an easier, out-of-the-box way...
I think the only solution will be initialization of fragment from onInit method of controller:
sap.ui.controller("my.controller", {
onInit : function(){
var oLayout = this.getView().byId('mainLayout'), //don't forget to set id for a VerticalLayout
oFragment = sap.ui.xmlfragment(this.fragmentName.bind(this));
oLayout.addContent(oFragment);
},
fragmentName : function(){
return "my.fragment";
}
});
The fragment name can also result from a binding, including an expression binding which evaluates to a constant. As formatter functions return strings, and not booleans, === 'true' has been added in the following example:
Example: Dynamic Fragment Name
<core:Fragment fragmentName="{= ${path: 'facet>Target', formatter: 'sap.ui.model.odata.AnnotationHelper.isMultiple'} === 'true'
? 'sap.ui.core.sample.ViewTemplate.scenario.TableFacet'
: 'sap.ui.core.sample.ViewTemplate.scenario.FormFacet' }" type="XML"/>