"onBeforeRendering" or "onAfterRendering" is not called every time the view is opened
sapui5
Solution
- 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 ..
},
}
Problem
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?