When the checkbox on the top node is clicked multiple times (quite quickly), the checkboxes of the child nodes start to "flicker".
Steps to reproduce the behavior:
No "flickering" should occur.
App
Device:
The problem seems to get worse the more components are present on the page. As it might be difficult to reproduce the issue on another environment, I'm attaching a screen capture from my machine.
I do see the issue in your stackblitz. But looking at the template I wonder, is there a reason you put the *ngFor on an ng-container? When I move it to the <clr-tree *ngFor="let group of userTree"> element iI can't get the issue to occur. https://stackblitz.com/edit/clarity-dark-theme-v1-0-treeview-issue-tyfovk
Thanks for your reply @hippee-lee. You are right, the ng-container is not necessary. However, even with your improved stackblitz, I can STILL reproduce the issue (although maybe less often).
As a workaround, is it possible to hide the checkbox on the top node?
Your tree uses *clrIfExpanded to lazy-load children. As explained in https://clarity.design/documentation/tree-view#lazy-loading-selection, we cannot propagate selection from parent to child or vice-versa with lazy-loading, because we only have partial information.
Our initial tries at propagating before we released 1.0 created a loop of issues where solving one would reopen the next, and it was all due to the fact that we cannot propagate selection to children that are hidden inside of a *clrIfExpanded. So we simply accepted the fact that we either handle selection for you, or you have to handle propagation if you want to use lazy-loading.
EDIT: You can even see the completely broken behavior if you collapse a group first, then select it, then expand it again. The children are simply not selected at all.
In your case, removing the <ng-template [(clrIfExpanded)]> solves the issue. If the performance of the tree in your app without [clrIfExpanded] is good enough, then that's what I would recommend doing. Otherwise, you will need to follow the instructions here and there, adding [clrLazy]="true" to your tree and ensuring yourself that children of a group are marked as selected when a group becomes selected.
Finally, you could also check out our recursive tree (either lazy-loaded or not) to see if it better matches your use case.
I'm closing this since the combination of lazy loading and "managed" selection is not something that's possible to support due to partial information.
Thank you @youdz for your help. Yes, #clrIfExpanded is not necessary for my project as I have the data readily available. Confusion on my part, sorry.
However, I have updated my Stackblitz so it is very similar to the documentation sample Binding selection to a boolean, and I still see the "flickering".
I would appreciate if you could take another look at it. Am I still missing something?
You're right, I can still get it to flicker, I just have to click faster.
I'm thinking this is a bug due to asynchronous event emitters, where the change detection happens before all of them have had time to emit their value. Really weird. That's why I don't like asynchronous event emitters, but in this case we don't have a choice. 馃槥
Reopening so we can investigate properly, thanks for the updated StackBlitz. 馃憤
FWIW, I've observed the behavior when there were 25-30 items on the list. Like you said, I believe that this happens when the user clicks the list parent checkbox again before the list can complete the click-all event from the previous click. Anything less than that it was fine. More than 25 you can trigger the bug due to the browser's time in delay of the clicking.
There are two ways to solve this and both have to do with promises or introducing a timed delay before the button is clicked again.
1) Promises: You can do a promise queue with a click event that will inject additional steps before the user is allowed to repeat an action:
a) checked.value = !checked.value
b) disable parent checkbox
c) wait until list is toggled
d) enable parent checkbox
2) Timed delay: Do a similar task with disabling the parent and multiply the delay by some factor against the number of checkboxes in the list before reenabling the button again:
disable parentcheckbox["id"]
checked.value = !checked.value
setTimeout(() => {}, 0.35 * n-many-checkboxes)
enable parentchekbox["id"]
This issue is also causing me grief. Here's an example: Stackblitz example
I tried another approach where I don't mutate the tree but assign a new object, but that has other odd behavior where selection doesn't seemingly propagate down to children: Stackblitz example
I spent some time reproducing this on our local dev app and investigating. As I suspected, making the (clrSelectedChange) EventEmitter synchronous solves the flickering problem. It also introduces Expression Has Changed errors which is the reason we made it asynchronous in the first place.
What that tells me is that the flickering is due to Angular鈥檚 asynchronous event emitters (which we do not control) not being able to fire all the events in a single change detection cycle, thus triggering multiple change detection cycles with a partial state updated for each.
The problem with the solutions you suggested above is that:
If based on my explanation above anyone has an idea for a solution, I鈥檒l be happy to take a look at it and help you contribute. Or I also might have just missed some aspect of the issue and there is indeed an obvious solution. That would be awesome.
In the meantime, this will be prioritized like any other issue we have, because the investigation itself of a potential fix is long enough that we have to schedule time for it during our plannings. (That's what our "Needs investigation" label means, by the way.)
We are also hitting the issue, I've implemented a work-around for the moment.
Hi all, I am working on a fix and am validating it against the various reproductions here. PR is up at #3227
@gssjr You are also affected by this same bug, but also are not setting the selection correctly for all of the child nodes on your own. The second example (without mutating the tree) is better, but the expression changed errors are on your side.
Hi there 馃憢, this is an automated message. To help Clarity keep track of discussions, we automatically lock closed issues after 14 days. Please look for another open issue or open a new issue with updated details and reference this one as necessary.
Most helpful comment
I spent some time reproducing this on our local dev app and investigating. As I suspected, making the
(clrSelectedChange)EventEmittersynchronous solves the flickering problem. It also introduces Expression Has Changed errors which is the reason we made it asynchronous in the first place.What that tells me is that the flickering is due to Angular鈥檚 asynchronous event emitters (which we do not control) not being able to fire all the events in a single change detection cycle, thus triggering multiple change detection cycles with a partial state updated for each.
The problem with the solutions you suggested above is that:
If based on my explanation above anyone has an idea for a solution, I鈥檒l be happy to take a look at it and help you contribute. Or I also might have just missed some aspect of the issue and there is indeed an obvious solution. That would be awesome.
In the meantime, this will be prioritized like any other issue we have, because the investigation itself of a potential fix is long enough that we have to schedule time for it during our plannings. (That's what our "Needs investigation" label means, by the way.)