Router Won't Load View Due to Missing "controlAggregation" - sapui5

I'm currently working on a web app based on SAPUI5. At the moment, it's based on HTML pages but we have to migrate to a dynamic structure. The solution will be the router. I've followed tons of tutorials, tried different structures (JS / XML view) but the router always throws an error saying:
The target intro has a control id or a parent but no 'controlAggregation' was set, so the target could not be displayed. - EventProvider sap.m.routing.Target
This is my current router setup:
"routing": {
"config": {
"routerClass": "sap.m.routing.Router",
"viewType": "XML",
"viewPath": "xx.xxxxxxx.view",
"controlId": "appContainer"
},
"routes": [
{
"pattern": "",
"name": "intro",
"target": "intro"
}
],
"targets": {
"intro": {
"viewName": "intro"
}
}
}
The "controlId" points to the <App> control in my "rootView".
I tried with very simple view (like Text only), but the problem persists.
<mvc:View
controllerName="xx.xxxxxxx.controller.app"
xmlns="sap.m"
xmlns:mvc="sap.ui.core.mvc"
displayBlock="true"
>
<App id="appContainer"/>
</mvc:View>

As the error message indicates, the router doesn't know into which aggregation the "intro"-view should be added. Since the control App has an aggregation named "pages" (inherited from NavContainer), include "controlAggregation": "pages" to the attribute "routing"/"config".

Related

Routing View Loading

I currently try to learn UI5 and right now the concept of routing. My current use case is, that the user selects one radio button out of a group and then presses a button. On my second view it should show the selected radio button.
I've set up both views, the routes and the targets. But it somehow does not want to load the View. If I have a look at the URL it enters the pattern of the route into it but it does not load the view - is there anything I need to do?
The rounter is initialized in the component.js
After I clicked the button the following method gets called:
this.getOwnerComponent().getRouter().navTo("overview");
this is my routing config:
"flexEnabled": false,
"rootView": {
"viewName": "com.enh.ess.com.enh.ess.view.Booking",
"type": "XML",
"async": true,
"id": "Booking"
},
// some stuff in between
"routing": {
"config": {
"routerClass": "sap.m.routing.Router",
"viewType": "XML",
"async": true,
"viewPath": "com.ess.view",
"controlAggregation": "pages",
"controlId": "app",
"clearControlAggregation": false,
"bypassed": {
"target": "noFound"
}
},
"routes": [
{
"name": "home",
"pattern": "",
"target": "booking_tar"
},
{
"name": "overview",
"pattern": "overviewBooking",
"target": "Overview"
}
],
"targets": {
"booking_tar": {
"viewType": "XML",
"transition": "slide",
"viewId": "Booking",
"viewName": "Booking"
},
"Overview": {
"viewType": "XML",
"viewName": "Overview"
},
"notFound": {
"viewId": "notFound",
"viewType": "XML",
"viewName": "NotFound",
"transition": "show"
}
}
}
my rootView ( Booking.view.xml ) is built like this ( I've removed some stuff here, like the attributes for the buttons or the path of the contrllerName - and I added one "comment" for the button ):
<mvc:View controllerName="" xmlns:mvc="sap.ui.core.mvc" displayBlock="true" xmlns="sap.m" xmlns:layout="sap.ui.layout">
<Shell id="shell">
<App id="app">
<pages>
<Page id="page" title="{i18n>title}">
<content>
<layout:VerticalLayout>
<RadioButtonGroup id="" columns="1"/>
<layout:HorizontalLayout>
<Button /> <-- when this button gets clicked it should load the new view
<Button />
</layout:HorizontalLayout>
</layout:VerticalLayout>
</content>
</Page>
</pages>
</App>
</Shell>
</mvc:View>
in my view Booking.view.xml I have the button. After I click it, it should navigate to the overview target / the Overview.viewl.xml - and as I said, it writes the value into the URL, but it doesn't load the view.
Do you have any idea what I also need to do? I've tried some things ( like view levels, that didn't work )
I am currently working in the WebIDE - if this is relevant for this.
thank you!
I suggest you to use a different strategy for the XML "encapsulation" to better exploit the routing mechanism: create an (almost) empty root view, such as App.view.xml, as follows:
<mvc:View xmlns:mvc="sap.ui.core.mvc" xmlns="sap.m" displayBlock="true">
<App id="idAppControl">
<pages>
<!-- Routed views are added here -->
</pages>
</App>
</mvc:View>
Modify your manifest accordingly
In /sap.ui5/rootView:
{
"viewName": "your.namespace.view.App",
"type": "XML",
"async": true
},
and in /sap.ui5/routing/config:
{
"routerClass": "sap.m.routing.Router",
"viewType": "XML",
"async": true,
"viewPath": "your.namespace.view",
"controlAggregation": "pages",
"controlId": "idAppControl"
},
Please note that the controlId and the ID of the <App> must be the same.
Now you've prepared the "container" for your routes and your Views can be simplified as follows:
<mvc:View controllerName="your.namespace.controller.Booking"
xmlns="sap.m"
xmlns:mvc="sap.ui.core.mvc">
<Page title="title" class="sapUiResponsiveContentPadding">
<content>
<!-- Your content here -->
</content>
</Page>
</mvc:View>
Same structure also for your detail page. Please remember that your Home page should have an empty routing pattern ("pattern" : "") in order to be the first hit route.
To learn more, take a look at the tutorial Navigation and Routing.

Why it does not show two columns?

I am trying to use Flexible Column Layout on my app and I have trouble with the routing. However it does not show the expected result on the screen.
The routing is defined as follows:
"routing": {
"config": {
"routerClass": "sap.f.routing.Router",
"viewType": "XML",
"async": true,
"viewPath": "com.example.RequestAccess.view",
"controlId": "fcl",
"transition": "slide",
"bypassed": {}
},
"routes": [{
"pattern": "jobprofile",
"name": "jobprofile",
"target": [
"JobProfileSelector",
"JobProfileOrder"
]
}, {
"pattern": ":layout:",
"name": "authorize",
"target": [
"Authorization"
]
}],
"targets": {
"Authorization": {
"viewName": "Authorization",
"controlAggregation": "beginColumnPages"
},
"JobProfileOrder": {
"viewName": "JobProfileOrder",
"controlAggregation": "midColumnPages"
},
"JobProfileSelector": {
"viewName": "JobProfileSelector",
"controlAggregation": "beginColumnPages"
}
}
}
},
when I do the call
var oRouter = this._getRouter();
oRouter.navTo("jobprofile");
it shows one column layout instead two columns. I expect to be two columns, because I have defined:
"pattern": "jobprofile",
"name": "jobprofile",
"target": [
"JobProfileSelector",
"JobProfileOrder"
]
Two targets for the path /jobprofile.
I expect, that the app should show like this:
What am I doing wrong?
Update
I have created an example app https://github.com/softshipper/fclpoc.
Click on Go to jobprofile
and you will redirect to page /jobprofile path. Here I expect, that it is going to show me Second and Third view next to each other, like master detail view.
You have to adjust property layout of your FlexibleColumnLayout in order to have 1, 2 or 3 columns. Default is 1.
In your root view, you already have a binding on this property, which is a good idea.
<FlexibleColumnLayout id="fcl"
stateChange="onStateChanged"
layout="{/layout}"
backgroundDesign="Solid">
</FlexibleColumnLayout>
However, you are not calling function _updateLayout from the App.controller.js anywhere.
I suggest refactoring this controller to adjust the layout based on the active route.

Why Is "target" an Array?

I have the following route definition:
"routes": [{
"name": "overview",
"pattern": "",
"target": [
"overview"
]
}, {
"name": "customers",
"pattern": "/customers",
"target": [
"customers"
]
}, {
"name": "customer",
"pattern": "/customer/{id}",
"target": [
"customer"
]
}],
Why does the target property expect an array?
The value of the target property can be an array but it doesn't have to be, as stated in the API reference of sap.ui.core.Route:
The reason why it can await an array at all, is to support displaying multiple view (target) instances at once when navigating to a single route / navigating based on a certain hash value.
Source: Routing and Navigation
Example: The app is based on the Flexible Column Layout (formerly "Split-Screen" layout with sap.m.SplitApp) and the user visits it via a deep link having the hash value already e.g. #/Objects/ObjectID_14.
Source code
In this case, if only a single target name were assigned to the target property, only one of the views would have been able to be displayed. The target property can, however, contain multiple target names which will be then resolved and put based on the controlAggregation accordingly.
"routes": [
{
"name": "masterDetail",
"pattern": "Objects/{objectId}",
"target": [
"master",
"detail"
]
}
],
"targets": {
"master": {
"name": "Master",
"controlAggregation": "beginColumnPages"
},
"detail": {
"name": "Detail",
"controlAggregation": "midColumnPages"
}
}
To learn more about routing, please follow these steps: https://ui5.sap.com/#/topic/1b6dcd39a6a74f528b27ddb22f15af0d
Because a route could have multiple targets. This is the equivalent of the former subroutes.
By using multiple targets for a single route you can update different areas of your application at the same time resp. based on the same routing pattern.
Think of a SplitApp. If you want to change as well Master and Detail view based on routing you can define both of them as targets of the same route.
Find more info in the docs: https://sapui5.hana.ondemand.com/#/topic/b01840ec42ef48e6bfd2bc12612f501f

Why Is My Main Controller Being Called Twice?

All other routings are fine, but for some reason the main controller is being called twice. Why would this happen?
onInit: function() {
var oRouter = this.getOwnerComponent().getRouter();
oRouter.getRoute("main").attachMatched(this._onRouteMatched, this);
this.getView().setModel(new JSONModel({
Jobs: []
}), "job");
},
Is this down to the routing config?
"rootView": {
"viewName": "CompleteSurvey.view.Main",
"type": "XML"
},
"routing": {
"routes": [{
"name": "main",
"pattern": "",
"target": ["main"]
}],
"config": {
"routerClass": "sap.m.routing.Router",
"viewType": "XML",
"viewPath": "CompleteSurvey.view",
"controlId": "app",
"controlAggregation": "pages"
},
"targets": {
"main": {
"viewName": "Main"
}
}
}
The reason why your Main controller is created twice is because its view is created twice.
Your Component fetches manifest.json and looks at the rootView to create the assigned view ("CompleteSurvey.view.Main").
Your router gets initialized, sees that the current hash / pattern is "", and creates the corresponding view which is the "Main" view again.
The current best practice is to have a separate root view. You can keep the Main for the "" pattern, but avoid using the same view again as a root view.
For further references, take a look at the tutorial Navigation and Routing and this answer.

Controller "Controller name" couldn't be instantiated SAPUI

I'm getting an error while I'm trying to navigate to another page in the sapui5 application. I'm trying to figure out what I am doing wrong. The directory Structure of my application is as below:
In manifest file
"routes": [{
"pattern": "",
"name": "appHome",
"target": "home"
}
, {
"pattern": "Course/{courseId}",
"name": "course",
"target": "course"
}],
"targets": {
"home": {
"viewName": "Home",
"viewLevel" : 1
},
"notFound": {
"viewName": "NotFound",
"transition": "show"
} ,
"course": {
"viewPath": "dlesDLESdashboard.view.Course",
"viewName": "CourseDetail",
"viewLevel" : 2
}
}
},
When I click on my navigate button I am triggering this event by default I'm giving static ID later I'll change.
handleCourseClick : function(oEvent){
this.getRouter().navTo("course",{courseId :"222222"});},
I'm not sure this information is good enough to find out my error or not.
The Error is that when I click on the tile I'm getting this error instead of navigating to my course detail page.
Check your controller, there might be something wrong with your view and controller connection. check namespace, name of controller etc and verify it.
sap.ui.define([
"sap/ui/model/json/JSONModel",
"locations/util/Utils",
"locations/controller/BaseController"
], function(BaseController, JSONModel, Utils) {
I declared these in the wrong order, and caused the same problem.