[英]angular async pipe not updating the view
我的问题可以通过与ngrx文档的选择器示例进行类比来最好地描述,以使事情保持简单( https://github.com/ngrx/platform/blob/master/docs/store/selectors.md#using-selectors-for多件状态 )。
我使用异步管道来订阅某些状态切片,例如使用选择器进行选择
this.visibleBooks$ = this.store$.select(selectVisibleBooks)
问题是,如果allBooks
数组是“ small”(小于100个项目),则我的视图会立即更新。 但是,当它大于100时,我的视图仅在下次触发更改检测(例如通过滚动)时才更新。 这是非常糟糕的用户体验,仅在滚动列表后才能看到书籍。
我看了异步管道的源代码( https://github.com/angular/angular/blob/master/packages/common/src/pipes/async_pipe.ts ),实际上_updateLatestValue
方法调用_updateLatestValue
ChangeDetectorRef.markForCheck()
,据我所知,它会在下次触发更改检测时将要检查的组件标记为更改。
我目前的解决方法是在顶级组件中手动订阅
this.store$.select(selectVisibleBooks).subscribe(cb)
并在回调中手动调用ChangeDetectorRef.detectChanges()
。
我发现这并不令人满意,并且无论Book[]
数组有多大,都希望异步管道始终运行。 有人对我可以做一些建议或纠正吗?
根据要求编辑
如上所述,上面的“书店”案例只是我为简化程序而编写的应用程序的类比。 实际上,我的应用程序渲染图的节点和边缘,其中节点和边缘还附加有一个称为“ vnode”的版本,该版本与“ vedge”一起跨越了一个版本树。 因此,任何图元素都有其自己的版本树。
我当前正在开发的是一个搜索表单,我们在其中向后端发送特定请求,以向其询问与一组特定搜索键/值对匹配的任何节点。
因此,这些节点将随后呈现在组件<nodes-list>
,我们通过输入绑定传递节点
<nodes-list [nodes]="nodes$ | async"></nodes-list>
nodes-list
具有“按入”更改检测功能,而顶级<search>
组件具有默认策略。
在ngOnInit()
中将nodes$
设置为
this.nodes$ = this.store$.select(selectFullNodesList)
selectFullNodesList
看起来像这样:
export const fullNodesSelector = getFullNodesSelector(createSelector(selectSearchState, s => {
if (s.currentId) {
const nodes = s.queries.get(s.currentId).nodes;
if (nodes) {
return [...nodes];
}
}
return null;
}))
export const selectFullNodesList = createSelector(
fullNodesSelector,
(global: GlobalState) => global.data.counts,
createSelector(selectSearchState, s => s.sort),
(nodes, counts, sorting) => {
if (!nodes || !counts || !sorting) return null;
return [...nodes.sort(sorting.sortCbFactory(counts))];
}
)
让我解释:
getFullNodesSelector(...)
我将在下面显示,它位于顶级库中,因为我们可能会在许多功能中重复使用它。 但是它的作用是,它以另一个选择器作为参数,该选择器指向节点和vnode密钥对{key: number, vKey: number}[]
数组,并将该数组转换为附加了vnode的节点数组(请参阅下面的方法)。 search
功能的状态,如果存在currentId
,这是当前请求到后端的ID,那么我们选择作为当前请求结果的节点。 s.queries
是一个围绕Javascript对象的轻量包装程序,它使我可以轻松获取/设置值,克隆或向克隆添加新项目。 当在NGRX中使用键/值存储时,这对我很有帮助。 因此, s.queries.get(s.currentId).nodes
。 global.data.counts
只是每个节点有多少个邻居的列表。 我想知道这一点,因为我想按“计数”对节点列表进行排序。 s.sort
是当前选择列表的排序方式。 sortCbFactory
,该工厂仅返回正确的回调以传递给Array.sort
,但我需要在回调的本地范围内显示counts
,因为否则我将无法按计数进行排序。 selectSearchState
只是功能选择器
export const selectSearchState = createFeatureSelector<SearchState>('search');
getFullNodesSelector(...)
看起来像这样:
function getFullNodesSelector(keyPairsSelector: MemoizedSelector<object, GraphElementKeyPair[]>): MemoizedSelector<object, INodeJSON<IVNodeJSON>[]> {
return createSelector(
keyPairsSelector,
(s: GlobalState) => s.data.nodes,
(s: GlobalState) => s.data.vnodes,
(pairs, nodes, vnodes) => {
if (!pairs || !nodes || !vnodes) return null;
return pairs.map(pair => ({
...nodes.get(pair.key),
_SUB: {
...vnodes.get(pair.vKey)
}
}));
})
}
再次发表一些评论:
GraphElementKeyPair
( {key: number, vKey: number}
)数组 get
方法。 因此,由于我们已经使用异步管道订阅了this.nodes$
,因此每次流<nodes-list>
上有一个新事件都应进行更新。 但是,实际上,这似乎取决于INodeJSON<IVNodeJSON>[]
的大小,并且如果数组的长度> INodeJSON<IVNodeJSON>[]
,我们必须通过单击某个地方或滚动来手动触发更改检测。 对于较小的数组,应该自动刷新节点列表。
大型数据集不会有任何问题。 您的选择器应该是同步的。 这意味着选择器在运行时,在后台没有其他任何事情发生。 不管在选择器中计算所有内容需要花费多少时间,都可以。 如果花费的时间太长,您的浏览器可能会死机,仅此而已。
当你说
我使用了ngrx-store-freeze,它应该防止出现这种情况
It is not true.
从商店的角度来看,这是事实。 但是,让我们想象一下:
您的商店有一个ID数组(比方说用户ID)。
您有一个名为getAllUsers
的第一个选择器。
这只是映射用户ID并检索正确的用户,对吗? 签名为(usersIds: string[]): User[]
。
当然,在这里,您将创建一个新的数组引用,并且(不应该)使usersIds
数组发生突变。
但是,然后,您有了另一个选择器。 getUsersResolved
基本上可以“解析”外部属性。 假设用户有动物列表。 从第一个选择器中,您将获得一个用户列表,每个用户都有一个animalsIds
属性。 但是,您想要的是改为具有animals
数组。 如果从该选择器中ngrx-store-freeze
了原始数组(来自第一个选择器的数组),则ngrx-store-freeze
将不会引发任何错误,这是有道理的:您正在更改一个数组,但不是存储中的一个数组。
那怎么可能是个问题呢?
getUsersResolved
,将其分配给一个变量,然后使用async
管道从视图中订阅该变量(假设这是您第一次在整个应用程序中订阅它!) getAllUsers
由getUsersResolved
调用(首次)(也首次调用) getAllUsers
按预期创建一个新数组,并将其传递给getUsersResolved
。 因为这是第一次,即使您将该数组修改为getUsersResolved
,您也不会有任何问题:更改检测将在第一次接收该数组时完成 getUsersResolved
将被触发,但是如果您不尊重不变性并修改来自getAllUsers
的第一个数组,则该数组引用不会更改,并且更改检测也不会发生。 而且,因为该数组不是商店的一部分,这是一个很好的选择,它是由选择器创建的数组 因此,我不确定您的问题是否来自那里,但是您可能要仔细检查一下您是否尊重选择器中的不变性。
最终,如果不确定,请共享selectVisibleBooks
的代码, 以及它使用的每个选择器 。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.