I set up the Application in a shell as follows in the index.html
new sap.m.Shell({
app : new sap.ui.core.ComponentContainer({
height : "100%",
name : "xx.xx.xx"
})
}).placeAt("content");
Is it possible to also add the application header control to this?
//create the ApplicationHeader control
var oAppHeader = new sap.ui.commons.ApplicationHeader("appHeader");
//configure the branding area
oAppHeader.setLogoSrc("http://www.sap.com/global/images/SAPLogo.gif");
oAppHeader.setLogoText("Logo Text");
//configure the welcome area
oAppHeader.setDisplayWelcome(true);
oAppHeader.setUserName(userFullName);
//configure the log off area
oAppHeader.setDisplayLogoff(true);
oAppHeader.placeAt("content");
When I added that before the Shell or in the main, it appears fine but the screen size has increased beyond 100% and so you have to scroll to see the footer (or the application header, depending where it's placed)
This is the Main.view.xml & Main.controller.js
<core:View xmlns:core="sap.ui.core"
xmlns="sap.m" controllerName="emc.ui.sc.view.Main"
displayBlock="true" height="100%" >
<App id="idMain">
</App>
</core:View>
sap.ui.controller("emc.ui.sc.view.Main", {
onInit : function() {
if (sap.ui.Device.support.touch === false) {
this.getView().addStyleClass("sapUiSizeCompact");
}
}
});
The Shell is - as the name indicates - intended to be used as root control. So the ApplicationHeader must be contained in the Shell. Why don't you use
the Unified Shell?
Related
How do I dynamically switch between two fragments using click / press event?
I have the following XML fragment which in turn has nested two fragments:
<core:FragmentDefinition
xmlns="sap.m"
xmlns:core="sap.ui.core">
<IconTabFilter
id="containerFrag"
text="{name}"
key="{id}">
<dependents>
<core:Fragment
fragmentName="com.example.fragments.fragment1"
type="XML" />
<core:Fragment
fragmentName="com.example.fragments.fragment2"
type="XML" />
</dependents>
</IconTabFilter>
</core:FragmentDefinition>
Assuming each fragment has a button who's handler has the following
buttonPress: function(oEvent) {
let oView = this.getView();
let showFrag1 = oEvent.getParameter("arguments"); //showFrag1 = true / false
let fragToShow = showFrag1 ? oView.byId("frag1Id").clone() : oView.byId("frag2Id").clone()
let container = oView.byId("containerFrag");
container.destroyContent();
container.addContent(fragToShow);
}
When I debug using chrome, fragToShow updates with the correct frag depending on the showFrag1 argument, but the view doesn't get updated - container.addContent(fragToShow) seems to have no effect except if I reload the page
Not sure why this works but I had to go one up from the container and insert the new fragment there like so:
let container = oView.byId("containerFrag");
container.getParent().destroyContent();
container.getParent().addContent(fragToShow);
This example also helped: https://ui5.sap.com/#/entity/sap.ui.layout.form.Form/sample/sap.ui.layout.sample.Form354 - though they use removeAllContent and insertContent, both seem to work just fine.
Thanks #D. Seah and #Ethan Jewett
I'm using a Split App which has
Master list
3 Detail Pages
MAster List shows a list of items ( here for example, bikes and cars list)
These are 3 detail pages:
Message page: for not found/welcome page when nothing is clicked on master list.
BikeProperties Page: which shows details if Bike is clicked.
CarProperties Page: which shows if Car is clicked.
Now, the issue is when I click on Car Product, there is an animation shown which navigates from say welcome page to car page.
However, if I click again a car product, binding is updated with no animation.
Similarly, if I select a Bike at this point ( after selecting car), the navigation happens with animation.
So, to summarize,
No animation is shown if same page is shown again in detail page.
Animation is shown when different detail page is loaded.
However, what I want is , irrespective of which detail page is currently shown, the navigation should happen again with animation so consistency with animation is maintained.
Also, please note that I cannot use hash based routing as this split app needs to be displayed in a dialog box.
Below is the dummy code:
App.view.xml
<mvc:View controllerName="com.sap.SplitApp.controller.App" xmlns:mvc="sap.ui.core.mvc" xmlns:core="sap.ui.core" displayBlock="true"
xmlns="sap.m">
<Shell id="shell">
<App>
<SplitApp id="app">
<masterPages>
<Page>
<List items='{/items}' selectionChange="handleNavigate" mode='SingleSelectMaster'>
<items>
<StandardListItem title='{name}' info='{type}'/>
</items>
</List>
</Page>
</masterPages>
<detailPages>
<MessagePage title='Hello!' text='Select a Product'></MessagePage>
<core:Fragment fragmentName="com.sap.SplitApp.fragments.BikeProperties" type="XML"/>
<core:Fragment fragmentName="com.sap.SplitApp.fragments.CarProperties" type="XML"/>
</detailPages>
</SplitApp>
</App>
</Shell>
App.controller.js
onInit: function () {
var items = [
{
name: 'Thunderbird 500cc',
type:'Bike'
},
{
name: 'Swift',
type:'Car'
},
{
name: 'Bullet 350cc',
type:'Bike'
},
{
name: 'Polo',
type:'Car'
}
];
var oModel = new sap.ui.model.json.JSONModel({ items: items});
this.getView().setModel(oModel);
},
handleNavigate: function(oEvent) {
var oBindingContext = oEvent.getParameter("listItem").getBindingContext();
var oSplitApp = this.byId("app");
var oDetailPage = null;
if (oBindingContext.getProperty("type") === "Bike") {
oDetailPage = this.byId('bikePage');
} else {
oDetailPage = this.byId('carPage');
}
oDetailPage.setBindingContext(oBindingContext)
oSplitApp.toDetail(oDetailPage);
}
BikeProperties.fragment.xml
<core:FragmentDefinition
xmlns="sap.m"
xmlns:mvc="sap.ui.core.mvc"
xmlns:l="sap.ui.layout"
xmlns:f="sap.ui.layout.form"
xmlns:core="sap.ui.core"
>
<Page id='bikePage' title='Bike'>
<Title text='{name}' />
</Page>
</core:FragmentDefinition>
CarProperties.fragment.xml
<core:FragmentDefinition xmlns="sap.m" xmlns:mvc="sap.ui.core.mvc" xmlns:l="sap.ui.layout" xmlns:f="sap.ui.layout.form"
xmlns:core="sap.ui.core">
<Page id='carPage' title='Car'>
<Title text='{name}'/>
</Page>
So since the API does not offer an obvious way to force an animaton, I looked at the source files.
Your problem is probably from this line from NavContainer.js:
var oFromPage = this.getCurrentPage();
if (oFromPage && (oFromPage.getId() === pageId)) { // cannot navigate to the page that is already current
Log.warning(this.toString() + ": Cannot navigate to page " + pageId + " because this is the current page.");
return this;
}
As you can see from the comment, it is not intended to navigate/animate to the same page that is currently displayed.
One possible solution would be to use a second fragment for both car and bike and to navigate to car2 if you were to be on car1 previously, then to car2 again and so on. This is the best workaround I found.
The following are just some things I found and might be worth to take a further look at, but I couldn't get it to work properly.
I found another line in the source that could be used but there is a catch. The page title bar does not slide as expected. It gets invisible as you can see with this snippet added to your controller in the handleNavigate function:
var oFromPage = oSplitApp.getCurrentDetailPage();
if (oBindingContext.getProperty("type") === "Bike") {
oDetailPage = this.byId("bikePage");
if (oFromPage === oDetailPage) {
sap.m.NavContainer.transitions.slide.to.call(this, this.byId("carPage"), this.byId("bikePage"), function callback() {});
}
} else {
oDetailPage = this.byId("carPage");
if (oFromPage === oDetailPage) {
sap.m.NavContainer.transitions.slide.to.call(this, this.byId("bikePage"), this.byId("carPage"), function callback() {});
}
}
I also noticed that styleClasses are being used for proper transition. The fromPage gets some styles and the toPage too. But since in your case fromPage and toPage are the same, the styleClasses cannot be applied/removed as needed.
oToPage.addStyleClass("sapMNavItemSliding").addStyleClass("sapMNavItemCenter").removeStyleClass("sapMNavItemRight");
oFromPage.addStyleClass("sapMNavItemSliding").removeStyleClass("sapMNavItemCenter").addStyleClass("sapMNavItemLeft");
Using only the "oFromPrage-styleClasses" for your detailsPage results in some sort of "bouncing" from the left side. Using all, one after another, results in wrong navigation.
Maybe you can make some use of these information but as already said, using two car fragments and two bike fragments was the best solution (user-experience-wise) I found.
Have you tried to pass the animation type to the.toDetailPage() method?
https://sapui5.hana.ondemand.com/#/api/sap.m.SplitContainer/methods/toDetail
It accepts a transitionname parameter, right after the page id, and I think this should be working.
Quote from the site:
"The type of the transition/animation to apply. This parameter can be omitted; then the default is "slide" (horizontal movement from the right). Other options are: "fade", "flip", and "show" and the names of any registered custom transitions.
None of the standard transitions is currently making use of any given transition parameters."
I have read a number of posts regarding destroying the fragment or page to avoid duplicate ID but the problem here is that a fragment is displayed in the view page and when i press the same button again I get the error Duplicate ID.
Below is the code for fragment and controller :
<core:FragmentDefinition xmlns="sap.m"
xmlns:f="sap.ui.layout.form" xmlns:core="sap.ui.core" xmlns:mvc="sap.ui.core.mvc"
xmlns:html="http://www.w3.org/1999/xhtml" xmlns:l="sap.ui.layout">
<Page id="masterAp" showHeader="false" enableScrolling="true" >
<TileContainer
id="getTiles"
tiles="{myModel1>/0/TileCollection}">
<StandardTile
title="{myModel1>title}"
icon="{myModel1>icon}"
number="{myModel1>number}"
numberUnit="{myModel1>numberUnit}"
info="{myModel1>info}"
infoState="{myModel1>infoState}"
press="handleTilePress"/>
</TileContainer>
</Page>
</core:FragmentDefinition>
The code for controller : Please advice what line of code is missing to avoid duplicate ID error : Uncaught Error: Error: adding element with duplicate id 'getTiles'
onPressGoToMaster1 : function() {
var oDialogFragment = sap.ui.xmlfragment("yca_web111_dashboard.fragments.panel2",this.getView().getController());
var oModel = new sap.ui.model.json.JSONModel("data/dataAP.json");
sap.ui.getCore().setModel(oModel, "myModel1");
var oPage = this.getView().byId("detail");
oPage.insertContent(oDialogFragment);
this.getSplitAppObj().to(this.createId("detail"));
};
You are trying to add the fragment every time you move to page with id: detail. Simplest solution would be to check if you have already added the fragment.
If yes (fragment is present), don't add the fragment again to page with id: detail, and navigate.
If no (fragment is not present), fetch the fragment, add to page and the navigate.
Code:
onPressGoToMaster1 : function() {
if (!this.oDialogFragment) {
this.oDialogFragment = sap.ui.xmlfragment("yca_web111_dashboard.fragments.panel2",this.getView().getController());
var oModel = new sap.ui.model.json.JSONModel("data/dataAP.json");
sap.ui.getCore().setModel(oModel, "myModel1");
var oPage = this.getView().byId("detail");
oPage.insertContent(this.oDialogFragment);
}
this.getSplitAppObj().to(this.createId("detail"));
};
I have a List Report which, once selected an item, prompts to an Object Page. I am using no FIORI Elements, everything was created from scratch.
The object page has a static header, but its body changes from item to item. In essence, the body uses different fragments that depends on a field (Position Type) of the selected item. In other words:
Pos Type 1 ---> fragment A
Pos Type 2 ---> fragment B
To do all this, on the controller of the object page, I have implemented the following withing the onBeforeRendering lifecycle method:
onBeforeRendering: function() {
// // Set Fragment to be used
var oLayout = this.getView().byId("ObjectPageLayout"),
oFragment = sap.ui.xmlfragment(this._fragmentName());
oLayout.addSection(oFragment);
},
_fragmentName: function() {
var oModel = this.getView().getModel();
var sPosType = oModel.getProperty(this.getView().getObjectBinding().getPath() + "/PositionType");
var sFragment;
if (sPosType === "1") {
sFragment = "A";
} else if (sPosType === "2") {
sFragment = "B";
}
return sFragment;
},
The problem I am facing is that this code is throwing the following error message: "Uncaught (in promise) TypeError: Cannot read property 'getPath' of undefined"
The only way I found to make this to work is by, instead of using method onBeforeRendering, I used onInit. This way, getPath() works fine. But if the user goes back to the List Report, and then selects an item of a different Position Type, then the Object Page displays the same fragment used in the previous item selected.
In case you wonder, bellow you will find the object view:
<mvc:View height="100%" xmlns="sap.uxap" xmlns:core="sap.ui.core" xmlns:mvc="sap.ui.core.mvc" xmlns:m="sap.m" xmlns:semantic="sap.m.semantic"
xmlns:forms="sap.ui.layout.form" xmlns:layout="sap.ui.layout" controllerName="objectview.controller"
xmlns:aud="sap.uxap.sample.SharedBlocks.fragmentblocks">
<semantic:FullscreenPage id="page" navButtonPress="onNavBack" showNavButton="true" title="{i18n>ObjectPageTitle}">
<m:Page title="Object Page Title">
<m:content>
<ObjectPageLayout id="ObjectPageLayout">
<headerTitle>
<ObjectPageHeader id="ItemTitle" objectTitle="Item Title">
<actions>
Some actions defined
</actions>
</ObjectPageHeader>
</headerTitle>
<headerContent>
Some Header Content
</headerContent>
<sections>
</sections>
</ObjectPageLayout>
</m:content>
<m:footer>
<m:Bar>
<m:contentRight>
Buttons added to the Footer
</m:contentRight>
</m:Bar>
</m:footer>
</m:Page>
</semantic:FullscreenPage>
What happens is that each time you go to the page you add a new section on the container of sections.
You can remove all the existent sections before adding the new one.
oLayout.removeAllSections();
oLayout.addSection(oFragment);
I watched some tutorials about navigation + passing data between views, but it doesn't work in my case.
My goal is to achieve the follwing:
On the MainPage the user can see a table with products (JSON file). (Works fine!)
After pressing the "Details" button, the Details Page ("Form") is shown with all information about the selection.
The navigation works perfectly and the Detail page is showing up, however the data binding doesnt seem to work (no data is displayed)
My idea is to pass the JSON String to the Detail Page. How can I achieve that? Or is there a more elegant way?
Here is the code so far:
MainView Controller
sap.ui.controller("my.zodb_demo.MainView", {
onInit: function() {
var oModel = new sap.ui.model.json.JSONModel("zodb_demo/model/products.json");
var mainTable = this.getView().byId("productsTable");
this.getView().setModel(oModel);
mainTable.setModel(oModel);
mainTable.bindItems("/ProductCollection", new sap.m.ColumnListItem({
cells: [new sap.m.Text({
text: "{Name}"
}), new sap.m.Text({
text: "{SupplierName}"
}), new sap.m.Text({
text: "{Price}"
})]
}));
},
onDetailsPressed: function(oEvent) {
var oTable = this.getView().byId("productsTable");
var contexts = oTable.getSelectedContexts();
var items = contexts.map(function(c) {
return c.getObject();
});
var app = sap.ui.getCore().byId("mainApp");
var page = app.getPage("DetailsForm");
//Just to check if the selected JSON String is correct
alert(JSON.stringify(items));
//Navigation to the Detail Form
app.to(page, "flip");
}
});
Detail Form View:
<mvc:View xmlns:core="sap.ui.core" xmlns="sap.m" xmlns:f="sap.ui.layout.form" xmlns:html="http://www.w3.org/1999/xhtml" xmlns:mvc="sap.ui.core.mvc" controllerName="my.zodb_demo.DetailsForm">
<Page title="Details" showNavButton="true" navButtonPress="goBack">
<content>
<f:Form id="FormMain" minWidth="1024" maxContainerCols="2" editable="false" class="isReadonly">
<f:title>
<core:Title text="Information" />
</f:title>
<f:layout>
<f:ResponsiveGridLayout labelSpanL="3" labelSpanM="3" emptySpanL="4" emptySpanM="4" columnsL="1" columnsM="1" />
</f:layout>
<f:formContainers>
<f:FormContainer>
<f:formElements>
<f:FormElement label="Supplier Name">
<f:fields>
<Text text="{SupplierName}" id="nameText" />
</f:fields>
</f:FormElement>
<f:FormElement label="Product">
<f:fields>
<Text text="{Name}" />
</f:fields>
</f:FormElement>
</f:formElements>
</f:FormContainer>
</f:formContainers>
</f:Form>
</content>
</Page>
</mvc:View>
Detail Form Controller:
sap.ui.controller("my.zodb_demo.DetailsForm", {
goBack: function() {
var app = sap.ui.getCore().byId("mainApp");
app.back();
}
});
The recommended way to pass data between controllers is to use the EventBus
sap.ui.getCore().getEventBus();
You define a channel and event between the controllers. On your DetailController you subscribe to an event like this:
onInit : function() {
var eventBus = sap.ui.getCore().getEventBus();
// 1. ChannelName, 2. EventName, 3. Function to be executed, 4. Listener
eventBus.subscribe("MainDetailChannel", "onNavigateEvent", this.onDataReceived, this);)
},
onDataReceived : function(channel, event, data) {
// do something with the data (bind to model)
console.log(JSON.stringify(data));
}
And on your MainController you publish the Event:
...
//Navigation to the Detail Form
app.to(page,"flip");
var eventBus = sap.ui.getCore().getEventBus();
// 1. ChannelName, 2. EventName, 3. the data
eventBus.publish("MainDetailChannel", "onNavigateEvent", { foo : "bar" });
...
See the documentation here: https://openui5.hana.ondemand.com/docs/api/symbols/sap.ui.core.EventBus.html#subscribe
And a more detailed example:
http://scn.sap.com/community/developer-center/front-end/blog/2015/10/25/openui5-sapui5-communication-between-controllers--using-publish-and-subscribe-from-eventbus
Even though this question is old, the scenario is still valid today (it's a typical master-detail / n-to-1 scenario). On the other hand, the currently accepted solution is not only outdated but also a result of an xy-problem.
is there a more elegant way?
Absolutely. Take a look at this Flexible Column Layout tutorial: https://sdk.openui5.org/topic/c4de2df385174e58a689d9847c7553bd
No matter what control is used (App, SplitApp, or FlexibleColumnLayout), the concept is the same:
User clicks on an item from the master.
Get the binding context from the selected item by getBindingContext(/*modelName*/).
Pass only key(s) to the navTo parameters (no need to pass the whole item context).
In the "Detail" view:
Attach a handler to the patternMatched event of the navigated route in the Detail controller's onInit.
In the handler, create the corresponding key, by which the target entry can be uniquely identified, by accessing the event parameter arguments in which the passed key(s) are stored. In case of OData, use the API createKey.
With the created key, call bindObject with the path to the unique entry in order to propagate its context to the detail view.
The relative binding paths in the detail view can be then resolved every time when the detail page is viewed. As a bonus, this also enables deep link navigation or bookmark capability.
You can also set local json model to store your data, and use it in the corresponding views. But be sure to initialize or clear it in the right time.