Reputation: 14669
I have one Tab
control and there are two tabs in it. I have placed two Kendo Grid/Any component in that different tabs.
When I open/select the tab first time it render the related component/html in the DOM. To boost the performance I have placed ngIf
on tabs to show only active tab html. So now Dom
show only active tab html but now when I traverse to other tab and revisit previous tab it's component/content seems render again. I wants to stop this second time rendering.
Note: If I replace ngIf
with hidden
then it works as accepted but it cost to performance as so many watches and DOM connected with it.
Actually my main problem is that due to above issue when I navigate to tabs my grid scroll position set to top every time instead it should remain at same state
Below is the some part of the code which I have did.
If condition in tab content html(render only selected tab in DOM)
<div class="tab-heading-outer">
<div class="tab-heading">
<ul id="ulOpenedTabs" class="nav nav-tabs main-tabs" role="tablist">
<li>
<span class="ellipsis"> {{tab.Header}} </span>
</li>
</ul>
</div>
</div>
<div class="tab-content" *ngIf="tab.IsSelected">
<ng-content>
<!--Html goes here-->
</ng-content>
</div>
Upvotes: 9
Views: 5088
Reputation: 442
Can you use [hidden]="condition"
instead of *ngIf="condition"
?
*ngIf
when false
will remove the element it's on and all of its children elements from the DOM.
Whereas when [hidden]
is true
the html is still in the DOM, but it is not visible on the screen to the user at the time until [hidden]="false"
;
Upvotes: 1
Reputation: 128
I have created a directive to achieve exactly this goal. It's name is ngxCacheIf and it aims to achieve what you need.
The directive act as an ngIf
at first, then as a [hidden]
. So the content is rendered only once then it is hidden shown.
I used it in replacement of an ngSwitch in my case to keep the previously rendered content in place and save performances.
You would use it this way :
<div class="tab-content" *ngxCacheIf="tab.IsSelected">
<!--Html goes here-->
</div>
Here is a live example : https://ngx-cache-if.stackblitz.io
Here is the npm, I created it yesterday, so feel free to make any comment, there is also the github coming with it :
https://www.npmjs.com/package/ngx-cache-if
Upvotes: 2
Reputation: 12998
If you really don't want to remove the component from DOM and re-render it when you open the tab, another solution could be this:
[hidden]
.ChangeDetectorRef.detach()
when the tab is closed.ChangeDetectorRef.reattach()
when the tab is opened to get the newest data.Depending on how your component is implemented, this could solve your performance issues. Especially if you rely heavily on Observables and the async Pipe (as you should), there should be no computation if the template is not checked for changes.
To quote the documentation for detach()
:
Imagine the data changes constantly, many times per second. For performance reasons, we want to check and update the list every five seconds. We can do that by detaching the component's change detector and doing a local check every five seconds.
You would do the same, only not time-based but based on whether the tab is open or not.
All you have to do is inject the ChangeDetectorRef in the component constructor and hook the change detection up to an @Input()
property:
constructor(private changeDetector: ChangeDetectorRef) {}
@Input() set visible(visible: boolean) {
if(!visible) {
changeDetector.detach();
} else {
changeDetector.reattach();
}
}
Now you can control whether the component gets new data or not by simply setting [visible]="false"
or [visible]="true"
.
You can find another full example in the official documentation for the ChangeDetectorRef.
Upvotes: 5
Reputation: 34435
I don't know your implementation of tab, but why don't you use a variable indicating whether the tab has been selected at least once to render it?
<div class="tab-heading-outer">
<div class="tab-heading">
<ul id="ulOpenedTabs" class="nav nav-tabs main-tabs" role="tablist">
<li>
<span class="ellipsis" (click)="onTabSelected(tab)"> {{tab.Header}} </span>
</li>
</ul>
</div>
</div>
<div class="tab-content" *ngIf="renderedTabs[tab.index]" [hidden]="!tab.ISelected">
<ng-content>
<!--Html goes here-->
</ng-content>
</div>
//component.ts
private renderedTabs : boolean[] = new Array(numberOfTabsHere);
onTabSelected(tab: Tab)
{
this.renderedTabs[tab.index] = true;
}
The code above can be simplified if you can modify your implementation of tab to include directly the variable on the Tab object
<div class="tab-heading-outer">
<div class="tab-heading">
<ul id="ulOpenedTabs" class="nav nav-tabs main-tabs" role="tablist">
<li>
<span class="ellipsis" (click)="tab.rendered=true"> {{tab.Header}} </span>
</li>
</ul>
</div>
</div>
<div class="tab-content" *ngIf="tab.rendered" [hidden]="!tab.ISelected">
<ng-content>
<!--Html goes here-->
</ng-content>
</div>
Upvotes: 2
Reputation: 38683
Note: If I replace ngIf with hidden then it works as accepted but it cost to performance as so many watches and DOM connected with it.
Yes, that would be a perfect solution for your issue. But if you are getting any performance issue, then you should need manually set the scroll potion from previous potion to the grid which is taken (use) by
temp
variable.
You may need this solution as well : how to move div scroll position based on button click in angular 2
Upvotes: 0