-
Notifications
You must be signed in to change notification settings - Fork 6.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(tabs): add ability to lazy load tab content #8921
Changes from 1 commit
4b19c58
17715ec
36f2dd2
02241d9
3b0e4e8
b66b2d7
fc450ed
65e423b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -288,3 +288,17 @@ <h1>Tabs with autosize textarea</h1> | |
</div> | ||
</mat-tab> | ||
</mat-tab-group> | ||
|
||
<h1>Lazy Loaded Tabs</h1> | ||
<mat-tab-group> | ||
<mat-tab label="First"> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would be neat to see a third tab that doesn't use lazy-loading so we can see the difference somehow (e.g. the content of Counter component should have the time it was loaded?) |
||
<ng-template matTabContent> | ||
<counter></counter> | ||
</ng-template> | ||
</mat-tab> | ||
<mat-tab label="Second"> | ||
<ng-template matTabContent> | ||
<counter></counter> | ||
</ng-template> | ||
</mat-tab> | ||
</mat-tab-group> |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -133,3 +133,16 @@ export class RainyTabContent {} | |
template: 'This is the routed body of the foggy tab.', | ||
}) | ||
export class FoggyTabContent {} | ||
|
||
@Component({ | ||
moduleId: module.id, | ||
selector: 'counter', | ||
template: `<span>{{count}}</span>` | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @amcdnl this is a presubmit error in Google, so if you can fix that it should unblock moving forward There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed. |
||
}) | ||
export class Counter { | ||
count = 0; | ||
ngOnInit() { | ||
this.count++; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not totally sure what the count represents, shouldn't this be 1 for any component regardless of how it is loaded? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To be honest, Its been so long I forgot what I was using it for too. I removed it. |
||
console.log('Counting...', this.count); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you provide some comments about why we are |
||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
/** | ||
* @license | ||
* Copyright Google Inc. All Rights Reserved. | ||
* | ||
* Use of this source code is governed by an MIT-style license that can be | ||
* found in the LICENSE file at https://angular.io/license | ||
*/ | ||
|
||
import {Directive, TemplateRef} from '@angular/core'; | ||
|
||
@Directive({selector: '[matTabContent]'}) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Needs some doc describing the use and purpose of this directive |
||
export class MdTabContent { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There are some more |
||
constructor(public template: TemplateRef<any>) { } | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,6 +18,7 @@ describe('MatTabGroup', () => { | |
AsyncTabsTestApp, | ||
DisabledTabsTestApp, | ||
TabGroupWithSimpleApi, | ||
TemplateTabs, | ||
], | ||
}); | ||
|
||
|
@@ -375,6 +376,23 @@ describe('MatTabGroup', () => { | |
}); | ||
}); | ||
|
||
describe('lazy loaded tabs', () => { | ||
it('should lazy load the second tab', async () => { | ||
let fixture = TestBed.createComponent(TemplateTabs); | ||
fixture.detectChanges(); | ||
|
||
let tabLabel = fixture.debugElement.queryAll(By.css('.mat-tab-label'))[1]; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Rename to |
||
tabLabel.nativeElement.click(); | ||
fixture.detectChanges(); | ||
|
||
fixture.whenStable().then(() => { | ||
fixture.detectChanges(); | ||
let child = fixture.debugElement.query(By.css('.child')); | ||
expect(child.nativeElement).toBeDefined(); | ||
}); | ||
}); | ||
}); | ||
|
||
/** | ||
* Checks that the `selectedIndex` has been updated; checks that the label and body have their | ||
* respective `active` classes | ||
|
@@ -602,3 +620,19 @@ class TabGroupWithSimpleApi { | |
}) | ||
class NestedTabs {} | ||
|
||
@Component({ | ||
selector: 'template-tabs', | ||
template: ` | ||
<mat-tab-group> | ||
<mat-tab label="One"> | ||
Eager | ||
</mat-tab> | ||
<mat-tab label="Two"> | ||
<ng-template matTabContent> | ||
<div class="child">Hi</div> | ||
</ng-template> | ||
</mat-tab> | ||
</mat-tab-group> | ||
`, | ||
}) | ||
class TemplateTabs {} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -24,6 +24,7 @@ import { | |
import {CanDisable, mixinDisabled} from '@angular/material/core'; | ||
import {Subject} from 'rxjs/Subject'; | ||
import {MatTabLabel} from './tab-label'; | ||
import { MdTabContent } from './tab-content'; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No spaces between brackets? |
||
|
||
|
||
// Boilerplate for applying mixins to MatTab. | ||
|
@@ -45,8 +46,11 @@ export class MatTab extends _MatTabMixinBase implements OnInit, CanDisable, OnCh | |
/** Content for the tab label given by <ng-template mat-tab-label>. */ | ||
@ContentChild(MatTabLabel) templateLabel: MatTabLabel; | ||
|
||
/** User provided template that we are going to use instead of implicitContent template */ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. "Template provided in the tab content that will be used if present, used to enable lazy-loading" |
||
@ContentChild(MdTabContent, {read: TemplateRef}) _explicitContent: TemplateRef<any>; | ||
|
||
/** Template inside the MatTab view that contains an <ng-content>. */ | ||
@ViewChild(TemplateRef) _content: TemplateRef<any>; | ||
@ViewChild(TemplateRef) _implicitContent: TemplateRef<any>; | ||
|
||
/** The plain text label for the tab, used when there is no template label. */ | ||
@Input('label') textLabel: string = ''; | ||
|
@@ -102,6 +106,7 @@ export class MatTab extends _MatTabMixinBase implements OnInit, CanDisable, OnCh | |
} | ||
|
||
ngOnInit(): void { | ||
this._contentPortal = new TemplatePortal(this._content, this._viewContainerRef); | ||
this._contentPortal = new TemplatePortal( | ||
this._explicitContent || this._implicitContent, this._viewContainerRef); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -80,6 +80,30 @@ The `tab-nav-bar` is not tied to any particular router; it works with normal `<a | |
the `active` property to determine which tab is currently active. The corresponding | ||
`<router-outlet>` can be placed anywhere in the view. | ||
|
||
## Lazy Loading | ||
By default, the tab contents are eagerly loaded. Eagerly loaded tabs | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we mention here about how lazy-loading resolves issues with components that expect to be in the DOM while they setup? e.g. expansion panels and nested ink bars. I suspect we'll want a place to point people to as we continue to get issues about this There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Preferably yes. I'm waiting on a fix to #5269, to render expansion panels within tabs....as are others. I'll probably use lazy loading in combination with a the Progress Spinner component since I render many expansion panels within a tab (see EDIT view on JSONSchema.Net). Eagerly awaiting this feature :) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @andrewseguin - Added, let me know what u think? |
||
will initalize the child components but not inject them into the DOM | ||
until the tab is activated. | ||
|
||
If the tab contains several complex child components, it is advised | ||
to lazy load the tab's content. Tab contents can be lazy loaded by | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this statement should stand out a bit more, even just by making it a new paragraph: "Tab contents can be lazy loaded by declaring the body in a |
||
declaring the body in a `ng-template` with the `matTabContent` attribute. | ||
|
||
```html | ||
<md-tab-group> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
<md-tab label="First"> | ||
<ng-template matTabContent> | ||
The First Content | ||
</ng-template> | ||
</md-tab> | ||
<md-tab label="Second"> | ||
<ng-template matTabContent> | ||
The Second Content | ||
</ng-template> | ||
</md-tab> | ||
</md-tab-group> | ||
``` | ||
|
||
### Accessibility | ||
Tabs without text or labels should be given a meaningful label via `aria-label` or | ||
`aria-labelledby`. For `MatTabNav`, the `<nav>` element should have a label as well. | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Needs consistent two-space indentation