问题描述
在我的 UI5 应用程序中,我有一个带有表 (sap.m.Table
) 的视图,由来自后端的数据填充在 onInit
挂钩上.问题是 onInit
每个视图实例只执行一次:
与 onBeforeRendering
和 onAfterRendering
钩子不同,每个 View 实例只调用一次.
如果用户决定离开这个视图(例如,返回导航)并稍后重新打开它,onInit
将不会被调用,因此数据将不会被再次检索,并且表格内容不会反映可能的变化.
为了确保每次打开视图时都检索到数据,我尝试在onBeforeRendering
处获取数据,但是这个钩子也只调用了一次.我发现,强制每次打开视图时都调用 onBeforeRendering
的唯一方法是将以下代码添加到 onInit
方法中:
onInit: function () {this.getView().addEventDelegate({onBeforeShow: this.onBeforeShow,}, 这);}
我的问题:
为什么没有
onInit
中的上述代码片段,每次显示视图时都不会触发onBeforeRendering
?上面的代码片段到底做了什么?
替代技术:使用
patternMatched
和routeMatched
.但这三种方法中哪一种更常见?
我认为对什么是渲染"存在误解.方法.在 UI5 中,当控件正在渲染"时,RenderManager
正在 DOM 中修改或创建其相应的 HTML 元素.IE.onBeforeRendering
字面意思是在调用控件(此处:View)的 render
函数之前".
onBeforeRendering
not 意味着它在浏览器的 paint 事件之前调用(为此,现代浏览器提供高级 API,例如作为交叉路口观察员).
渲染的控件可以在 DOM 中,但不能同时在视口中可见.
回到问题;之所以没有触发on*Rendering
,是因为控件之前已经渲染过了.当用户通过 navTo
导航然后再次返回时,可以看到这一点.对应的视图元素已经在DOM中,所以不需要再次调用render
,意味着没有触发on*Rendering
.
- 代码片段究竟做了什么?
this.getView().addEventDelegate({onBeforeShow: this.onBeforeShow,}, 这);
addEventDelegate
向在控件上触发的事件添加一个监听器(不是由控件触发).
例如:视图包含事件,如afterInit
、beforeExit
、...
执行 addEventDelegate({onAfterInit})
将不起作用,因为 afterInit
被此控件(视图)触发.
执行 addEventDelegate({onmouseover})
有效,因为它在这个控件上触发.
同样适用于 onBeforeShow
.该视图不包含任何事件,如 beforeShow
、afterShow
等.这些事件由 NavContainer<on 触发./code>(例如通过
在其子视图上).可以在以下位置找到有关这些事件的文档:
- API 参考:
sap.m.NavContainerChild代码>
- 主题在页面上触发的事件
另见类似问题https://stackoverflow.com/questions/44882085/why-does-onbeforefirstshow-work/44882676
- 替代技术:使用
patternMatched
(...).但是这三种方法中哪一种更常见?
通过三种方法"我假设你的意思是:
on*Rendering
(第一种方法),on*Show
(第二种方法),- 以及上面提到的路由事件,如
patternMatched
(第三种方法).
与往常一样,答案是这取决于您要实现的目标.但通常,我们:
如果应用程序没有路由概念(manifest.json 中没有
sap.ui5/routing
),请使用第二种方法(NavContainerChild
事件).如果意图是在视图显示后设置初始焦点,请使用带有
onAfterShow
的第二种方法.请参阅如何在视图中设置初始焦点?使用第 3 种方法获取有关正在匹配的路由模式的通知.这种方法通常用于在每次显示视图 (
NavContainerChild
) 时执行某些操作,例如执行 上下文绑定 导航到详细信息页面后.工作原理:- 当
router.navTo()
被调用时,下一个对应的视图和控制器被创建. - 在新创建的控制器的
onInit
中,您分配一个patternMatched
handler一>. - 在导航时,URL 哈希值会发生变化.路由器(内部为
HashChanger
) 注意到 URL 更改,导致路由触发patternMatched
将调用您的处理程序.请参阅下面的TL;DR.
- 当
⚠️ 个人意见:作为 应用程序 开发人员,避免第一种方法.避免在
onBeforeRendering
和onAfterRendering
中做任何事情,因为从应用程序的角度来看,render
函数被调用的频率是不可预测的.对于控制开发人员来说,这些钩子是绝对必要的.但对于应用程序开发人员来说,通常有更好的选择.
TL;DR
忘记on(Before|After)Rendering
.使用 (pattern)Matched
event 来自路由:
{//控制器onInit:函数(){const myRoute = this.getOwnerComponent().getRouter().getRoute(routeName");myRoute.attachPatternMatched(this.onMyRoutePatternMatched, this);},onMyRoutePatternMatched:函数(事件){//当视图即将显示时你的代码..},}
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:
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 theonBeforeRendering
not triggered every time the view is displayed?What does exactly the code snippet above do?
The alternative technique: to use
patternMatched
androuteMatched
. But which one of these three approaches is more common?
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.
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
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 (nosap.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 apatternMatched
handler. - On navigation, the URL hash value will change. The router (internally the
HashChanger
) notices the URL change, leading to Route firingpatternMatched
by which your handler will be invoked. See the TL;DR below.
- When
⚠️ Personal opinion: avoid 1st approach as an application developer. Avoid doing anything in
onBeforeRendering
and inonAfterRendering
since it's unpredictable how often therender
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: function() {
const myRoute = this.getOwnerComponent().getRouter().getRoute("routeName");
myRoute.attachPatternMatched(this.onMyRoutePatternMatched, this);
},
onMyRoutePatternMatched: function(event) {
// your code when the view is about to be displayed ..
},
}
这篇关于“onBeforeRendering"或“onAfterRendering"每次打开视图时都不会调用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!