Skip to content

feat(tabs): adds ability for lazy loaded tabs #6832

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

Closed
wants to merge 11 commits into from

Conversation

amcdnl
Copy link
Contributor

@amcdnl amcdnl commented Sep 4, 2017

This adds the ability for users to lazy load the contents of a tab.

The API for this is adding a new <ng-template mdTabContent> in the content of your tab with the tab's contents inside this tag.

<md-tab-group>
  <md-tab label="First">
    <ng-template mdTabContent>
      <counter></counter>
    </ng-template>
  </md-tab>
  <md-tab label="Second">
    <ng-template mdTabContent>
      <counter></counter>
    </ng-template>
  </md-tab>
</md-tab-group>

This addresses: #1854 and #5222
References: angular/angular#18691

@amcdnl amcdnl self-assigned this Sep 4, 2017
@amcdnl amcdnl requested a review from jelbourn September 4, 2017 16:49
@amcdnl amcdnl requested a review from andrewseguin as a code owner September 4, 2017 16:49
@googlebot googlebot added the cla: yes PR author has agreed to Google's Contributor License Agreement label Sep 4, 2017
Copy link
Member

@jelbourn jelbourn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need unit tests for this change

@@ -146,7 +146,8 @@ export class MdTabBody implements OnInit, AfterViewChecked {
*/
ngAfterViewChecked() {
if (this._isCenterPosition(this._position) && !this._portalHost.hasAttached()) {
this._portalHost.attach(this._content);
// Nested templates via mdTabContent templates causes expression change error
Promise.resolve().then(() => this._portalHost.attach(this._content));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we attach it in ngDoCheck instead? It would be better to avoid the extra change detection pass.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: After this change I had to update the unit test that checked tab body contents.

<ng-template><ng-content></ng-content></ng-template>
<ng-template #bodyTemplate>
<ng-content></ng-content>
<ng-template *ngIf="templateBody && templateBody.template"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why use ngTemplateOutlet here instead of just creating a TemplatePortal directly from the TemplateRef from mdTabContent?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You need the original outlet for the projection, once projected then your able to read the template out and attach. I could replace the original outlet once projected but I'd have to attach/detach/attach. Is that what your suggesting?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't really follow what you mean.

I was thinking something like

export class MdTab {
  @ContentChild(MdTabContent, {read: TemplateRef}) _explicitContent: TemplateRef<any>; 
  @ViewChild(TemplateRef) _implicitContent: TemplateRef<any>;
}

Then use _explicitContent if it is defined, otherwise use _implicitContent (with the same code path we currently have for _content).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right, querying works even though its not projected. Updated per feedback.

Copy link
Member

@jelbourn jelbourn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you also update the documentation in this PR?

if (this._isCenterPosition(this._position) && !this._portalHost.hasAttached()) {
// Nested templates via mdTabContent templates causes expression change error
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make this comment something like

// It is important to attach the view during `DoCheck`; if an embedded view is created during
// change detection, it will either cause a changed-after-checked error or never be checked at all.

@@ -0,0 +1,6 @@
import {Directive, TemplateRef} from '@angular/core';

@Directive({ selector: '[mdTabContent]' })
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Omit spaces in braces

TestBed.compileComponents();
}));

it('should lazy load the second tab', (done) => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you need done here instead of using async?

@amcdnl
Copy link
Contributor Author

amcdnl commented Sep 9, 2017

@jelbourn ready for another review.

Copy link
Member

@jelbourn jelbourn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@jelbourn jelbourn added pr: lgtm action: merge The PR is ready for merge by the caretaker labels Sep 10, 2017
@josephperrott josephperrott added pr: needs rebase and removed action: merge The PR is ready for merge by the caretaker labels Sep 15, 2017
@josephperrott
Copy link
Member

@amcdnl please rebase when you have a chance

@amcdnl
Copy link
Contributor Author

amcdnl commented Sep 17, 2017

@josephperrott - Merged :)

@josephperrott josephperrott added action: merge The PR is ready for merge by the caretaker and removed pr: needs rebase labels Sep 18, 2017
@jelbourn jelbourn added the P1 Impacts a large percentage of users; if a workaround exists it is partial or overly painful label Sep 18, 2017
@josephperrott
Copy link
Member

josephperrott commented Sep 21, 2017

Look like this actually is causing a failure in a specific test case. Here is a minimal repro spec:

import {Component} from '@angular/core';
import {CommonModule} from '@angular/common';
import {async, ComponentFixture, TestBed, flushMicrotasks, fakeAsync} from '@angular/core/testing';
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
import {MdTabsModule} from './public_api';


describe('MdTabs', () => {
  let fixture: ComponentFixture<TestTabs>;
  let element: HTMLElement;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      imports: [CommonModule, MdTabsModule, BrowserAnimationsModule],
      declarations: [TestTabs]
    });
    TestBed.compileComponents();
  }));


  beforeEach(() => {
    fixture = TestBed.createComponent(TestTabs);
    element = fixture.nativeElement;
    fixture.detectChanges();
  });

  it('will pass after click.', () => {
    const firstTab = element.querySelectorAll('.mat-tab-label')[0] as HTMLElement;
    firstTab.click();
    fixture.detectChanges();

    expect(element.querySelectorAll('.mat-tab-body')[0].querySelectorAll('.items').length).toBe(1);
  });
  it('won\'t pass without click.', () => {
    expect(element.querySelectorAll('.mat-tab-body')[0].querySelectorAll('.items').length).toBe(1);
  });
});


@Component({
  template: `
    <md-tab-group>
      <md-tab label="tab 1">
        <p class="items"> content for tab 1</p>
      </md-tab>
      <md-tab label="tab 2">
        <p class="items"> content for tab 2</p>
      </md-tab>
    </md-tab-group>
  `
})
class TestTabs {}

I am fairly confident this is because on initial load, the DoCheck lifecycle hits before the view is initialized which causes it not attach the portalHost on the first round of change detection.

…bs-template

# Conflicts:
#	src/demo-app/demo-app/demo-module.ts
#	src/lib/tabs/public_api.ts
#	src/lib/tabs/tab.ts
@andrewseguin andrewseguin added pr: needs rebase and removed action: merge The PR is ready for merge by the caretaker labels Sep 29, 2017
@amcdnl
Copy link
Contributor Author

amcdnl commented Oct 3, 2017

Waiting on @josephperrott changes on #7266 to be merged before rebasing.

@jelbourn jelbourn added P2 The issue is important to a large percentage of users, with a workaround and removed P1 Impacts a large percentage of users; if a workaround exists it is partial or overly painful labels Oct 24, 2017
@jelbourn
Copy link
Member

Downgrading this to P2 since it can go in a minor release

@josephperrott
Copy link
Member

@amcdnl rebase whenever you have a chance :)

@josephperrott josephperrott added the target: minor This PR is targeted for the next minor release label Dec 6, 2017
# Conflicts:
#	src/demo-app/tabs/tabs-demo.html
#	src/lib/tabs/public_api.ts
#	src/lib/tabs/tab-body.ts
#	src/lib/tabs/tab-group.spec.ts
#	src/lib/tabs/tab-group.ts
#	src/lib/tabs/tab.ts
#	src/lib/tabs/tabs-module.ts
#	src/lib/tabs/tabs.md
@amcdnl
Copy link
Contributor Author

amcdnl commented Dec 10, 2017

@josephperrott I'm going to close this PR and open a new one given all the changes. See: #8921

@amcdnl amcdnl closed this Dec 10, 2017
@amcdnl amcdnl deleted the tabs-template branch December 10, 2017 19:52
@angular-automatic-lock-bot
Copy link

This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.

Read more about our automatic conversation locking policy.

This action has been performed automatically by a bot.

@angular-automatic-lock-bot angular-automatic-lock-bot bot locked and limited conversation to collaborators Sep 7, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
cla: yes PR author has agreed to Google's Contributor License Agreement P2 The issue is important to a large percentage of users, with a workaround target: minor This PR is targeted for the next minor release
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants