I am trying to navigate from one view to another in UI5 application but I am not able to do so. I recently created a copy of an existing view A and renamed it to B. Then I copied the controller of A and renamed it as controller of B. I now want to navigate from A to B view. I have declared the view B in routes just like A (renamed wherever necessary) but I am still not able to navigate. When I click on button in view A, I want to display B. But nothing happens. Console shows no error either. What can be the issue?
In Controller of A:
buttonClick: function(event) {
vc.getOwnerComponent().getRouter().navTo("stockDetails", {
"companyId": vc.companyId,
"stockroomId": vc.stockroomId,
"order": vc.orderNo
}, false);
},
In manifest.json:
Under routes:
{
"name": "stockDetails",
"pattern": "company/{companyId}/stockrooms/{stockroomId}/order/{order}",
"titleTarget": "",
"greedy": false,
"target": [
"menu",
"stockDetails"
]
}
Under targets:
"stockDetails": {
"viewType": "XML",
"transition": "slide",
"clearAggregation": true,
"viewName": "stockroom.stockDetails", //stockDetails View B
"viewLevel": 2,
"controlAggregation": "pages",
"controlId": "app"
}
Here are some possible causes..
Multiple Targets for <App>
It looks like you're trying to assign multiple targets ("menu", "stockDetails") to the same aggregation ("pages") at the same time. Unless it's using master-detail layout or a parent target is involved, try to assign only the right target for the right aggregation. In case of sap.m.App:
{
"name": "stockDetails",
"pattern": "...",
"target": "stockDetails"
}
Otherwise, you'll encounter unexpected behaviors depending on the implementation of the routerClass module.
Identical Patterns
Although the route name may be different ("stockDetails"), you probably have kept the route pattern when copy-pasting from the source route (A).
If it's true, you'll need to provide a distinct pattern for the route "stockDetails" as well. When identical patterns exist, the first route is always taken into account over the others.*
The sequence of the routes in the routes definition is important. As soon as a pattern is matched, the following patterns are ignored.[src]
Here, you can try to define two identical patterns and set the hash accordingly. You'll see that only the first pattern is matched which would be, in your case, the view A.
* Unless the property greedy is active in one of the other routes.
Not sure if you are using the right event for button click.
Can you try using the "press" event, and not the "buttonClick" event?
Before that, try putting a "console.log("Event Firing OK") within your buttonClick event handler, and check the console if the message is coming up. If not, you know the event is not even getting fired, and hence, your navigation code is never executed.
REFERNCE : https://sapui5.hana.ondemand.com/#/api/sap.m.Button/events/press
Best Regards,
Gopal Nair.
Related
This question already has an answer here:
Duplicate ID error when creating fragments in different Controllers
(1 answer)
Closed 3 years ago.
I have an XML fragment which I have used in 2 different views. When I click on a table row for item A - view A opens and if I click on "Save" on that page - Fragment opens. Depending upon the action the user did on the Fragment (it has 2 buttons - Yes/ Cancel), next step happens but I close the fragment at the end of the process. If I click on Item B in the table - view B opens. If I click on "Save" on this page, expectation is the same fragment should open. But instead I get a duplicate ID error.
One observation: if I use a different Fragment (similar in design) in both, the controllers issue is resolved. Everything works fine. But if same, fragment error.
Not sure why close if not working correctly. Tried destroy() but then it gave me setInititalFocus() undefined something like that error.
Controller A.js Similar code is in ControllerB.js
onManageConfirmation: function(oEvent) {
var ccModelObj = {
"headerText": "Hello",
"operation": "deleteItem"
};
if (!this.ABC) {
var ccModel = new JSONModel(); // required "sap/ui/model/json/JSONModel"
this.ABC= sap.ui.xmlfragment("...fragments.commons.ManageConfirmation", this);
this.getView().addDependent(this.ABC);
this.ABC.setModel(ccModel);
}
this.ABC.getModel().setData(ccModelObj);
this.ABC.open();
},
onYes: function(oEvent) {
this.ABC.close();
},
onCancel: function(oEvent) {
// in fragment also added onClose property and calling this method only
this.ABC.close();
},
Just a wild guess here, but your this is referencing two different things in your controllers.
When your in controller A everything works like a charm but then you load controller B and the check for this.ABC returns false and your code tries to load a fragment that is already at the DOM hence the duplicate Id error.
Have you checked your fragment.xml if you have used any ids inside of it ? If yes you must ensure to destroy the fragment before opening it again.
In my UI5 app, I have a view with a table (sap.m.Table), populated by data coming from the back-end at onInit hook. The problem is that onInit is executed only once per view instance:
It is only called once per View instance, unlike the onBeforeRendering and onAfterRendering hooks.
And if a user decides to leave this view (e.g., back navigation) and to reopen it later, the onInit will not be recalled, and thus the data will not be retrieved again, and the table content will not reflect the possible changes.
To ensure that the data are retrieved every time the view is opened, I tried to get the data at onBeforeRendering, but this hook is also called just once. The only way, that I have found, to force onBeforeRendering to be called every time the view is opened, is to add the following code into onInit method:
onInit: function () {
this.getView().addEventDelegate({
onBeforeShow: this.onBeforeShow,
}, this);
}
My questions:
Why, without the code snippet above in onInit, is the onBeforeRendering not triggered every time the view is displayed?
What does exactly the code snippet above do?
The alternative technique: to use patternMatched and routeMatched. But which one of these three approaches is more common?
Why (...) is the onBeforeRendering not triggered every time the view is displayed?
I think there is a misconception of what "rendering" means. In UI5, when a control is "rendering", its corresponding HTML element is being modified or created in the DOM by the RenderManager. I.e. onBeforeRendering means literally "before the render function of the control (here: View) is called".
onBeforeRendering does not mean that it's called before the paint event from the browser (For that, modern browsers provide high-level APIs such as Intersection Observer).
Rendered controls can be in the DOM but not visible in the viewport at the same time.
Coming back to the question; the reason why on*Rendering is not triggered, is because the control has been already rendered before. This can be seen when user navigates via navTo and then back again. The corresponding view element is already in the DOM, so there is no need to call render again, meaning no on*Rendering triggered.
What does the code snippet exactly do?
this.getView().addEventDelegate({
onBeforeShow: this.onBeforeShow,
}, this);
addEventDelegate adds a listener to the events that are fired on the control (not by the control).
E.g.: The view contains events like afterInit, beforeExit, ...
Doing addEventDelegate({onAfterInit}) won't work since afterInit is fired by this control (view).
Doing addEventDelegate({onmouseover}) works since it's fired on this control.
The same applies to the onBeforeShow. The view doesn't contain any events like beforeShow, afterShow, etc.. Those are fired on the view by the NavContainer (e.g. by <App> on its child, view). Documentation about those events can be found in:
API reference: sap.m.NavContainerChild
Topic Events Fired on the Pages
See also a similar question https://stackoverflow.com/questions/44882085/why-does-onbeforefirstshow-work/44882676
The alternative technique: to use patternMatched (...). But which one of these three approaches is more common?
By "three approaches" I assume you mean:
on*Rendering (1st approach),
on*Show (2nd approach),
and the above mentioned routing events like patternMatched (3rd approach).
The answer is, as always, it depends on what you're trying to achieve. But usually, we:
Use the 2nd approach (NavContainerChild events) if the application does not have a routing concept (no sap.ui5/routing in manifest.json).
Use the 2nd approach with onAfterShow if the intent is to set initial focus after the view is displayed. See How to Set Initial Focus in a View?
Use the 3rd approach to get notified about the route pattern being matched. This approach is commonly used to do something every time the view (NavContainerChild) is displayed, for example, to do Context Binding after navigating to a detail page. How it works:
When router.navTo() is called, the next corresponding view and controller are created.
In onInit of the newly created controller, you assign a patternMatched handler.
On navigation, the URL hash value will change. The router (internally the HashChanger) notices the URL change, leading to Route firing patternMatched by which your handler will be invoked. See the TL;DR below.
⚠️ Personal opinion: avoid 1st approach as an application developer. Avoid doing anything in onBeforeRendering and in onAfterRendering since it's unpredictable how often the render function is called from the viewpoint of the application. For control developers, those hooks are absolutely necessary. But for application developers, there are often better alternatives.
TL;DR
Forget on(Before|After)Rendering. Use the (pattern)Matched event from the route instead:
{
// Controller
onInit() {
const myRoute = this.getOwnerComponent().getRouter().getRoute("routeName");
myRoute.attachPatternMatched(this.onMyRoutePatternMatched, this);
},
onMyRoutePatternMatched(event) {
// your code when the view is about to be displayed ..
},
}
You should use
onInit: function() {
let route = this.getOwnerComponent().getRouter().getRoute("yourroutename");
route.attachPatternMatched(this.onRoutePatternMatched, this);
// ...
},
This route event will be triggered every time the route pattern is matched in your routing config. In the above example, the function onRoutePatternMatched will be called every time the route is loaded through navigation.
onRoutePatternMatched: function(event) {
// this logic will repeat itself every time the route pattern is matched
},
Question #3:
patternMatched is going to get hit when your router is matched on the URL or on the router.navTo method.
routeMatched is going to get hit on the same occasion as patternMatched and when its child routes get navigated to.
Imagine, you have a master view on route A and it’s detail on route B. If the user navigates directly to route B, it makes sense to render the target associated to route B and also the target associated to route A.
To conclude:
patternMatched: direct route match
routeMatched:
The pattern of a route in this router.
The pattern of its sub-route.
The pattern of its nested route. When this occurs, the nestedRoute parameter is set with the instance of nested route.
The answers of Boghyon Hoffmann and Bernard are currently the best solution to the problem of needing an event to perform some action when the page is navigated to.
There is an option where it's possible to configure the target of a route to clear the control aggregation and force it to be re-rendered every time it is navigated to. With this configurantion the events onBeforeRendering and onAfterRendering will be called when navigating to the page. However, it is not a recommended solution as it will cause performance issues with the constant re-rendering and it should set to false when working with sap.m.NavContainer or it would cause transitions stop happening and other issues. If you are working on a legacy project that uses sap.ui.ux3.Shell, which is currently deprecated, it is a possible solution.
So to configure this option we need to set the option clearControlAggregation to true in the manifest.json, it is false by default when using sap.m.routing.Router.
In this link you can check all the options you have for the route targets:
https://sapui5.hana.ondemand.com/#/api/sap.ui.core.routing.Targets
"config": {
"routerClass": "sap.ui.core.routing.Router",
"viewPath": "TestApp.view",
"viewType": "XML",
"clearControlAggregation": true,
"controlId": "Main",
"controlAggregation": "pages",
"transition": "slide"
}
It is also possible to add this option only for a specific target like this:
"targets": {
"Test1": {
"viewType": "XML",
"viewName": "Test1",
"clearControlAggregation": true,
"title": "Test1"
},
"Test2": {
"viewType": "XML",
"viewName": "Test2",
"title": "Test2"
}
}
How is the jcr:language property is being configured in the below site structure, for example, on the node (/content/myapp/uk/en/jcr:content)?
{
"content": {
"myapp": {
"jcr:content" {
"us": {
"jcr:mixinTypes": "rep:AccessControllable",
"jcr:primaryType": "cq:Page"
},
"de": {
"jcr:mixinTypes": "rep:AccessControllable",
"jcr:primaryType": "cq:Page"
},
"fr": {
"jcr:mixinTypes": "rep:AccessControllable",
"jcr:primaryType": "cq:Page"
},
"uk": {
"jcr:mixinTypes": "rep:AccessControllable",
"jcr:primaryType": "cq:Page",
"jcr:content": {},
"en": {
"jcr:content": {},
}
}
}
}
}
}
This question is related to Invalid iso code stored in /content/myapp/uk/en: en_uk. I can remove the warnings mentioned in that question by changing the jcr:language value to en_GB manually in the page, the warnings are gone.
But I want to configure it in such a way that the correct jcr:language property should be automatically set to en_GB, while creating the language node itself (i.e en). Is this possible?
#raju muddana i would like to know your use case for automatically creating the jcr:language property.
Generally this language property is left over to content authors after your project implementation. in AEM any number of pages will be created with one template wich can be used for multi languages content authoring. Generally we can segregate this with content structures like an example shown in geometrix demo site (example: /content/geometrixx/en, /content/geometrixx/fr, etc.). once the content structure has been established content-authors can set this language property accordingly.
In addition to #i.net answer, just showing you that on any page in AEM by default you can add the language by using Page Properties Advanced Tab Language property. until unless if you don't customize your page properties.
Classic UI:
Touch UI:
Jcr:language Property
You can set the jcr:language to en_GB on /content/myapp/jcr:content/uk/jcr:content node. This will ensure that any pages below this node that don't have the jcr:language defined will pick this property from the parent node.
fancytree folders are never collapsed. All children and parents are displayed without correct nesting structure.
When I copy the exact same data that works in text data source, instead from a web2py (python) controller the folders will not collapse but just display permanently expanded. No js console errors in browser.
original data that works perfectly in text file
FancyTree copies data from python contoller like this
json_list = [{
"alexLink": "http://example.com/",
"kind": "tasks#task",
"id": "MTYwNzEzNjc2OTEyMDI1MzcwNzM6ODUwNjk4NTgzOjExMTkyODk2MjA",
"etag": "\"4qyCALf1j510T_-I20NAMbUHF2k/LTEzNTgzMTMzODg\"",
"title": "Task 01",
"updated": "2015-04-23T19:25:44.000Z",
"selfLink": "",
"position": "00000000002147483647",
"status": "needsAction"
}]
I convert to json: json_list = json.dumps(json_list)
Then use as source:
// Initialize Fancytree
$("#alexTree").fancytree({
checkbox: true,
selectMode: 3,
source: {{=XML(json_list)}},
postProcess: function(event, data){
data.result = convertData(data.response);
},
select: function(event, data) {
window.open(data.node.data.alexLink, "_blank");
Data looks same as in text file source. What could be causing the folders to not contract with children under them?
I can't see an obvious reason, why your sample is not working (I assume persistence extension is off?).
Except maybe for {{=XML(json_list)}}, that may do something unexpected.
I guess a debuggable demo is needed, to find out.
Anyway, your sample does this:
Generate tree data in a native (none-Fancytree) format
Generate a html page with an embedded <script> tag that in turn has this data embedded as string
On page-load the tree is created with native data and post processing is done client-side
Fancytree was designed with this pattern in mind (among others):
Have a static page with an empty <div id="tree"> element and include some JavaScript
On page-load initialize the tree and pass an ajax url for source.
This will immediately display the page for your users, while loading is deferred (showing a spinning icon)
A soon as the data arrives, the tree will update.
(You could also consider to do the conversion server-side and send valid Fancytree-compatible data.)
This would probably deliver a smoother user experience. It would also allow to refresh tree data without reloading the page, by calling tree.reload().
I need to modify the parser rules for my img element.
Currently it only support very limited attributes with limit data types as following:
"img": {
"check_attributes": {
"width": "numbers",
"src": "url",
"height": "numbers",
"data-sda": "url"
}
}
How to make it support more elements like class or id.
In my solution I, the user, drag images and drop it inside the editor which is wysihtml5 as I said before.
This pull request on github adds what seems like exactly the feature you're looking for: extend checkAttributes value type. It's a small change; you could do it by hand on your own copy without any fuss.