angular ui-grid selecting all under grouping
Asked Answered
T

3

14

https://jsfiddle.net/4byyuqtc/1/

enter image description here

I'm looking to have the ui-grid select all "children" under a grouping when the grouping line is selected. In this case Kit Kat(1), Mr. Goodbar(1), Krackel(2) and ultimately selecting the actual records (the non bold lines). One would expect that when selecting a parent in a grouping all it's children would get selected as well.

Currently when selecting the 1 grouping above the actual records in the data (the non bold lines) it does select those actual records with the following code:

$scope.gridApi.selection.on.rowSelectionChanged($scope, function (rowChanged) {
    console.log(rowChanged.treeLevel);
    if (typeof (rowChanged.treeLevel) !== 'undefined' && rowChanged.treeLevel > -1) {
        // this is a group header
        children = $scope.gridApi.treeBase.getRowChildren(rowChanged);
        console.log(children);
        children.forEach(function (child) {
            if (rowChanged.isSelected) {
                $scope.gridApi.selection.selectRow(child.entity);
            } else {
                $scope.gridApi.selection.unSelectRow(child.entity);
            }
        });
    }
});

I'm not experienced enough with ui-grid at this point to figure out how to cycle through children of the selected line and select all of them.

[EDIT]

With Paul's code below it doesn't select the groupings but it's closer. This screenshot is me selecting the first 337 record. Notice it selects that record and all the lowest child records (which is good because ultimately those are the ones that matter) but visually the grouped records (MFG and Item Desc group) aren't selected and need to be as the user won't ever open the lowest data records so they need to see the groups selected.

enter image description here

Tila answered 10/7, 2017 at 18:35 Comment(0)
H
8

I checked the documentation and I don't think there's any exposed API Method. You could recursively select/deselect rows as a solution. Please try out the example below.

$scope.gridApi.selection.on.rowSelectionChanged($scope, function (rowChanged) {
                    console.log(rowChanged.treeLevel);
                    if (typeof(rowChanged.treeLevel) !== 'undefined' && rowChanged.treeLevel > -1) {
                        var children = $scope.gridApi.treeBase.getRowChildren(rowChanged);
                        selectChildren(children, rowChanged.isSelected);
                    }
                });

function selectChildren(gridRows, selected) {
                        if (gridRows && gridRows.length > 0) {
                            gridRows.forEach(function (child) {
                                if (selected) {
                                    $scope.gridApi.selection.selectRow(child.entity);
                                } else {
                                    $scope.gridApi.selection.unSelectRow(child.entity);
                                }

                                var children = $scope.gridApi.treeBase.getRowChildren(child);
                                selectChildren(children, selected); //recursively select/de-select children
                            });
                        }
                    }

Here's a working Plunkr: http://plnkr.co/edit/XsoEUncuigj9Cad1vP5E?p=preview

Handling automatic deselection is a bit more tricky though as it seems the api doesn't handle that really well.

UPDATE

So I checked the jsFiddle you shared and managed to get it working with a slight tweak.

I modified the selectionHandler to the following:

onRegisterApi: function(gridApi) {
  $scope.gridApi = gridApi;
  $scope.gridApi.selection.on.rowSelectionChanged($scope, function(rowChanged) {
    if (rowChanged.treeNode.parentRow) { //Added this parent row selection
        rowChanged.treeNode.parentRow.setSelected(rowChanged.isSelected);
    }
    console.log(rowChanged.treeLevel);
    if (typeof(rowChanged.treeLevel) !== 'undefined' && rowChanged.treeLevel > -1) {
      var children = $scope.gridApi.treeBase.getRowChildren(rowChanged);
      selectChildren(children, rowChanged.isSelected);
    }
  });

Please see this fork of your code: https://jsfiddle.net/1eg5v77w/

The downside with this is that if you select a low level entry (one without children) it will still select its parent. If you really really want this to work as well, you'll have to access the DOM and make some ugly checks.

$scope.gridApi.selection.on.rowSelectionChanged($scope, function(rowChanged, $event) {
        var wasHeaderRowClicked = true;
        try {   //This can be written more beautifully if you used jQuery. But I would still be against it as it relies on the class of the ui-grid never changing when you update your ui-grid version.
          wasHeaderRowClicked = $event
          .srcElement
          .parentElement
          .parentElement
          .parentElement
          .previousElementSibling
          .firstChild
          .firstChild
          .firstChild
          .getAttribute('class') === 'ui-grid-icon-minus-squared';
        } catch(err) { console.log('Couldnt determine if header row was clicked'); }
        if (rowChanged.treeNode.parentRow && wasHeaderRowClicked) {
            rowChanged.treeNode.parentRow.setSelected(rowChanged.isSelected);
        }
        console.log(rowChanged.treeLevel);
        if (typeof(rowChanged.treeLevel) !== 'undefined' && rowChanged.treeLevel > -1) {
          var children = $scope.gridApi.treeBase.getRowChildren(rowChanged);
          selectChildren(children, rowChanged.isSelected);
        }
      });

Here is the fiddle: https://jsfiddle.net/Lf8p7Luk/1/

I'd also like to add, thanks to this post, that according to the UI-Grid documentation: Group header rows cannot be edited, and if using the selection feature, cannot be selected. They can, however, be exported.

So it is intentional that it's so difficult to get this to work because it's not the intended design. My recommendation would be to alter your logic to either use Tree Levels or get around the selection logic because even though my fork is currently selecting everything, you will most likely run into other issues down the road. For example: I couldn't get automatic deselection to work in the grid when you click on another group header.

Holliholliday answered 24/7, 2017 at 18:22 Comment(15)
So I tried the code but it's not working. The one difference I noticed is you added this $$treeLevel setting in the data. I don't have that and I wouldn't know that. The data can be grouped in any way the user decides at run-time. So I'm thinking this might be messing up my code that doesn't have/use that. I would need this to be grouping agnostic.Tila
The $$treeLevel is only there in the demo. The actual selection event handler does not use that property.Holliholliday
So in my app your code is selecting the lowest level correctly, but it's not selecting any grouping lines which would need to be selected too. I noticed in the log it prints that it's selecting some (the lowest level) but then I get 'undefined' printed a few times as well which must be the grouped lines.Tila
I will make a plunker that better matches my setup.Tila
Having issues making a plunker. I keep getting permissions denied when I try to save. I updated my original post with an image of what happens and a description when I apply your code above.Tila
As I step through this, it does call selectRow() on the grouped columns, but the thing I notice is when it calls selectRow() on the normal columns it raises the rowSelectionChanged() event for them instantly, but it doesn't do that for the grouped columns when they are passed to selectRow()Tila
I updated my answer with a fork of your jsfiddle, please take a lookHolliholliday
A nuance to this seems to be in order for it to be selected it has to be expanded, which isn't desired. I assume in your try statement too this is hardcoded to accept 3 levels of grouping vs any number of levels.Tila
I believe the "node needing to be expanded" is the behavior of the ui-grid. Because if you check, no selection event is triggered if the node is not expanded. I don't know how you'd get around that. Also the try statement has the group expand button's relative location hard coded. It doesn't have anything to do with the depth of your groups. You should be able to add rows as deep as you want.Holliholliday
So here is what is odd. In the jsfiddler I notice the not expanded line doesn't raise the event. In my code in my project the event is triggered when not expanded. It has the same version of angular and ng ui-grid so it's really strange. I see other different behavior as well. Like when I deselect a grouped line the lowest level data doesn't get deselected. Any ideas why I'd be seeing different behavior? It's not browser related. I use Visual Studio and pull from nuget vs cdn like jsfiddler but it's the same version.Tila
Maybe it's the jquery version? I think angular will use jquery if it's present and I have 3.1.1 in my visual studio project but just angular in the jsfiddler.Tila
I'm not sure why it's behaving differently but that just goes to show that there is no reliable solution to this issue imo. We're trying to hack our way into getting the grid to do something it was designed specifically not to do. I don't know if you're using aggregates; but if you're not, I'd just recommend changing your design.Holliholliday
I am using aggregates. The design calls for only caring about the sum of the product not all the details. I could maybe do the grouping on the data access side and then use straight grid and not use grouping on the grid perhaps. Ultimately all we're trying to do, which doesn't seem unreasonable, is get to checking the actual data so I can change a status field on them. The grouping just gives the users a flexible way to mass check these records.Tila
Before you mentioned you needed to select the group headers. But it seems like you just need to select the data records.Holliholliday
The group headers need to be selected for the visual part. If they aren't visually selected then what's under them won't seem like they are selected and will be confusing. The core app part only cares about the data records so it can change a value on them. Both are needed for a complete user experience.Tila
T
3

If you still have the issue take a look with this..

https://github.com/angular-ui/ui-grid/issues/3911

Ton answered 25/7, 2017 at 9:16 Comment(1)
One of those examples kind of works but only when I have 2 levels. My grouping is by 3 levels and it doesn't work at all at 3 levels.Tila
F
0

As none of the above answers worked for me, I followed @Deepak link in github and found an answer that is simple and works flawlessly for me.

(this is not my solution and I connected the link to this solution above, but I am posting it here for people to have easy access)

onRegisterApi: (gridApi) => {
    $scope.gridApi = gridApi
                    
    $scope.gridApi.selection.on.rowSelectionChangedBatch($scope, function (rows) {
        rows.forEach(childRow => $scope.proceedParentSelection(childRow));
    });
        
    $scope.gridApi.selection.on.rowSelectionChanged($scope, function (row) {
        $scope.proceedParentSelection(row);
    });
        
    $scope.proceedParentSelection = function(childRow) {
        let parentRow = childRow.treeNode.parentRow;
        parentRow.isSelected = parentRow.treeNode.children.every(child => child.row.isSelected)
    }
},
Fuchsin answered 3/6 at 13:28 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.