AngularJS + AG-格:粘/记得选择与虚拟分页/无限滚动 [英] AngularJS + ag-grid: sticky/remembered selections with virtual paging/infinite scrolling
问题描述
在一个AngularJS应用程序中,我有一个 AG-电网中使用的虚拟分页/无限滚动从数据集过大,显示延迟加载行立刻。我打开了复选框选择在第一列,这样,用户应当能够选择任意特定应用的操作的各个行。
In an AngularJS application, I have an ag-grid that uses virtual paging/infinite scrolling to lazy-load rows from a dataset that is too large to show at once. I have turned on check-box selection in the first column, so that the user should be able to select individual rows for arbitrary application-specific actions.
在AngularJS应用程序使用 UI路由器来控制多个视图。因此,建立在href=\"http://www.ag-grid.com/angular-grid-virtual-paging/index.php\" rel=\"nofollow\">与排序和放大器虚拟呼叫例如,有关奥运获奖者构建的数据,从 AG-格
的文件,我已经进一步扩大了codeA位。从 index.html的
:
The AngularJS application uses ui-router to control multiple views. So, building on the virtual-paging example with "sorting & filtering", with constructed data about Olympic winners, from the ag-grid
documentation, I've further extended the code a bit. From index.html
:
<body ng-controller="MainController" class="container">
<div ui-view="contents"></div>
</body>
和以下 UI路由器
规定:
myapp.config(function($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise("example.page1")
$stateProvider
.state('example', {
abstract: true,
views: {
contents: {
template: '<div ui-view="example"></div>'
}
}
})
.state('example.page1', {
url: '/page1',
views: {
example: {
templateUrl: 'page1.html'
}
}
})
.state('example.page2', {
url: '/page2',
views: {
example: {
template: 'Go back to the <a ui-sref="example.page1">example grid</a>.'
}
}
});
});
其中, page1.html
如下所示:
<div ng-controller="GridController">
<div ag-grid="gridOptions" class="ag-fresh" style="height: 250px;"></div>
</div>
<div>
<h3>Selected rows:</h3>
<ul class="list-inline">
<li ng-repeat="row in currentSelection track by row.id">
<a ng-click="remove(row)">
<div class="badge">#{{ row.id }}, {{ row.athlete }}</div>
</a>
</li>
</ul>
</div>
<p>Go to <a ui-sref="example.page2">the other page</a>.</p>
我要完成什么:
- ,在
AG-格
所做的选择是记得(粘)滚动(虚拟)页面的时候拿出来看,然后再返回,以便用户可以选择多个在单独的页面行。 - 那想起选项可用网格的外部,并且支持添加和删除选项(如预期由
NG-点击=删除(行)
在page1.html
,如上图所示)。 - 那应该选择从与
AG-格
到另一个视图切换时,并再次被记住。 - (可选),从而使选择的记住用户的会话。
- That selections made in the
ag-grid
is remembered (sticky) when scrolling a (virtual) page out of view and back again, so that a user can select multiple rows on separate pages. - That the remembered selections are available outside the grid, and support adding and removing selections (as intended by the
ng-click="remove(row)"
inpage1.html
, shown above). - That the selections should be remembered when switching from the view with the
ag-grid
to another one, and back again. - (Optional) That the selections are remembered for the user's session.
我怎样才能做到这一点?
How can I accomplish this?
推荐答案
我创建了一个的这个工作的例子可以实现。
I've created a working example of this can be implemented.
首先,我们将编写一个AngularJS服务, selectionService
来跟踪选择的方式:
First of all, we'll write a AngularJS service, selectionService
to keep track of the selections:
function _emptyArray(array) {
while (array.length) {
array.pop();
}
}
function _updateSharedArray(target, source) {
_emptyArray(target);
_.each(source, function _addActivity(activity) {
target.push(activity);
});
}
myapp.factory('selectionService', function ($rootScope, $window) {
var _collections = {},
_storage = $window.sessionStorage,
_prefix = 'selectionService';
angular.element($window).on('storage', _updateOnStorageChange);
function _persistCollection(collection, data) {
_storage.setItem(_prefix + ':' + collection, angular.toJson(data));
}
function _loadCollection(collection) {
var item = _storage.getItem(_prefix + ':' + collection);
return item !== null ? angular.fromJson(item) : item;
}
function _updateOnStorageChange(event) {
var item = event.originalEvent.newValue;
var keyParts = event.originalEvent.key.split(':');
if (keyParts.length < 2 || keyParts[0] !== _prefix) {
return;
}
var collection = keyParts[1];
_updateSharedArray(_getCollection(collection), angular.fromJson(item));
_broadcastUpdate(collection);
}
function _broadcastUpdate(collection) {
$rootScope.$emit(_service.getUpdatedSignal(collection));
}
function _afterUpdate(collection, selected) {
_persistCollection(collection, selected);
_broadcastUpdate(collection);
}
function _getCollection(collection) {
if (!_.has(_collections, collection)) {
var data = _loadCollection(collection);
// Holds reference to a shared array. Only mutate, don't replace it.
_collections[collection] = data !== null ? data : [];
}
return _collections[collection];
}
function _add(item, path, collection) {
// Add `item` to `collection` where item will be identified by `path`.
// For example, path could be 'id', 'row_id', 'data.athlete_id',
// whatever fits the row data being added.
var selected = _getCollection(collection);
if (!_.any(selected, path, _.get(item, path))) {
selected.push(item);
}
_afterUpdate(collection, selected);
}
function _remove(item, path, collection) {
// Remove `item` from `collection`, where item is identified by `path`,
// just like in _add().
var selected = _getCollection(collection);
_.remove(selected, path, _.get(item, path));
_afterUpdate(collection, selected);
}
function _getUpdatedSignal(collection) {
return 'selectionService:updated:' + collection;
}
function _updateInGridSelections(gridApi, path, collection) {
var selectedInGrid = gridApi.getSelectedNodes(),
currentlySelected = _getCollection(collection),
gridPath = 'data.' + path;
_.each(selectedInGrid, function (node) {
if (!_.any(currentlySelected, path, _.get(node, gridPath))) {
// The following suppressEvents=true flag is ignored for now, but a
// fixing pull request is waiting at ag-grid GitHub.
gridApi.deselectNode(node, true);
}
});
var selectedIdsInGrid = _.pluck(selectedInGrid, gridPath),
currentlySelectedIds = _.pluck(currentlySelected, path),
missingIdsInGrid = _.difference(currentlySelectedIds, selectedIdsInGrid);
if (missingIdsInGrid.length > 0) {
// We're trying to avoid the following loop, since it seems horrible to
// have to loop through all the nodes only to select some. I wish there
// was a way to select nodes/rows based on an id.
var i;
gridApi.forEachNode(function (node) {
i = _.indexOf(missingIdsInGrid, _.get(node, gridPath));
if (i >= 0) {
// multi=true, suppressEvents=true:
gridApi.selectNode(node, true, true);
missingIdsInGrid.splice(i, 1); // Reduce haystack.
if (!missingIdsInGrid.length) {
// I'd love for `forEachNode` to support breaking the loop here.
}
}
});
}
}
var _service = {
getCollection: _getCollection,
add: _add,
remove: _remove,
getUpdatedSignal: _getUpdatedSignal,
updateInGridSelections: _updateInGridSelections
};
return _service;
});
的 selectionService
服务允许添加和删除单独的集合,由确定集合
任意对象,你会发现一个名字适当。通过这种方式,相同的服务可以用于记忆在多个 AG-格
实例的选择。每个对象将使用路径
参数来确定。在路径
用于使用检索的唯一标识符lodash的获得一>功能。
The selectionService
service allows adding and removing arbitrary objects to separate collections, identified by collection
, a name you find suitable. This way the same service can be used for remembering selections in multiple ag-grid
instances. Each object will be identified using a path
parameter. The path
is used to retrieve the unique identifier using lodash's get function.
此外,该服务使用的sessionStorage 坚持用户的整个标签/浏览器会话中的选择。这可能是矫枉过正;我们可以只依靠服务来保持选择的赛道,因为它只会被实例化一次。这当然可以修改您的需求。
Furthermore, the service uses sessionStorage to persist the selections during the user's whole tab/browser session. This might be overkill; we could have just relied on the service to keep track of the selections since it will only get instantiated once. This can of course be modified to your needs.
然后有一个必须完成的 GridController
的变化。首先第一列所有 columnDefs
进入不得不稍微改变
Then there were the changes that had to be done to the GridController
. First of all the columnDefs
entry for the first column had to be changed slightly
var columnDefs = [
{
headerName: "#",
width: 60,
field: 'id', // <-- Now we use a generated row ID.
checkboxSelection: true,
suppressSorting: true,
suppressMenu: true
}, …
,其中,一旦数据已被从远程服务器检索产生新的,产生的行ID
where the new, generated row ID is generated once the data has been retrieved from the remote server
// Add row ids.
for (var i = 0; i < allOfTheData.length; i++) {
var item = allOfTheData[i];
item.id = 'm' + i;
}
(即M
中的ID刚刚列入,以确保我没有混淆的ID与 AG-用其他ID格
)
(The 'm'
in the ID was included just to make sure I didn't confused that ID with other IDs used by ag-grid
.)
接下来, gridOptions
必要的修改是添加
Next, the necessary changes to gridOptions
were to add
{
…,
onRowSelected: rowSelected,
onRowDeselected: rowDeselected,
onBeforeFilterChanged: clearSelections,
onBeforeSortChanged: clearSelections,
…
}
被不同的处理程序是相当简单的,与之通信的 selectionService
function rowSelected(event) {
selectionService.add(event.node.data, 'id', 'page-1');
}
function rowDeselected(event) {
selectionService.remove(event.node.data, 'id', 'page-1');
}
function clearSelections(event) {
$scope.gridOptions.api.deselectAll();
}
现在,在 GridController
需要处理由信号更新 selectionService
太
Now, the GridController
needs to handle updates signalled by the selectionService
too
$scope.$on('$destroy',
$rootScope.$on(selectionService.getUpdatedSignal('page-1'),
updateSelections));
和
function updateSelections() {
selectionService.updateInGridSelections($scope.gridOptions.api, 'id', 'page-1');
}
通话 selectionService.updateInGridSelections
将更新有关电网的并网选择。这是写最繁琐的功能。例如,如果选择了外部添加(网格外的),那么我们将不得不执行 forEachNode
运行,即使我们知道所有必要的节点已经在网格中选择;有没有办法提前退出的循环。
calls selectionService.updateInGridSelections
which will update the in-grid selections of the grid in question. That was the most cumbersome function to write. For example, if a selection has been added externally (outside the grid), then we'll have to perform a forEachNode
run, even if we know all the necessary nodes have already been selected in-grid; there's no way to exit that loop early.
最后,另一个关键是分别以清除和前后重新应用的选择,当过滤器或排序顺序被改变时,或者当新的数据被从服务器(其仅在演示模拟的)检索。解决的办法是包括 params.successCallback
中的 GetRows的<后
updateSelections
通话/ code>处理程序
Finally, another crux was to clear and reapply the selections before and after, respectively, when the filters or sort orders are changed, or when new data is retrieved from the server (which is only simulated in the demo). The solution was to include a call to updateSelections
after the params.successCallback
inside the getRows
handler
params.successCallback(rowsThisPage, lastRow);
updateSelections();
现在,该解决方案的实施过程中最令人费解的结果是, AG-格
API网格选项 onAfterFilterChanged
和 onAfterSortChanged
不能用于重新应用的选择,因为他们(远程)数据加载完成之前触发。
Now, the most puzzling findings during the implementation of this solution was that the ag-grid
API grid options onAfterFilterChanged
and onAfterSortChanged
couldn't be used for reapplying the selections because they trigger before the (remote) data has finished loading.
这篇关于AngularJS + AG-格:粘/记得选择与虚拟分页/无限滚动的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!