Components: [MdSidenav] - Sidenav Resizing Regression

Created on 30 Aug 2017  路  42Comments  路  Source: angular/components

Bug, feature request, or proposal:

Bug/Regression from 2.0.0-beta.8.

Also a feature request for an API to allow triggering recalculations for the MdSidenavContainer/MdDrawerContainer, or being able to update _styles manually and having the view update..

What is the expected behavior?

In the previous release 2.0.0-beta.8 the sidenav resized when the window was resized.

I have a responsive sidenav that uses Angular flex-layout for showing/hiding responsive content in the sidenav when the view changes from md to lg and vice-versa.

What is the current behavior?

In 2.0.0-beta.10 this is no longer the case. The changes made in #6189 made the sidenav update only when the sidenav is opening/closing.

Since flex-layout's changes occur without having to call open() or close() on the sidenav, the width recalculation doesn't occur.

example

What are the steps to reproduce?

Put dynamic/responsive content within a sidenav that is configured with "side".

What is the use-case or motivation for changing an existing behavior?

Being able to dynamically call a refresh of the MdSidenav/MdDrawer or MdSidenavContainer/MdDrawerContainer.

Even better would be an accessor to allow manually setting the margin of the sidenav to allow for pre-computing the _style variable and updating it when needed.

Which versions of Angular, Material, OS, TypeScript, browsers are affected?

Angular 4.3.5
Angular Material 2.0.0-beta.10

Is there anything else we should know?

This is similar to #6583.

For right now I am using the following hack in my navigation directive to workaround whenever the ObservableMedia from angular flex-layout changes:

// Timeout required for flex-layout directives to update the child views/the width.
setTimeout(() => {
            (this._container as any)._updateStyles();
            (this._container as any)._changeDetectorRef.markForCheck();
}, 0);
P1 has pr

Most helpful comment

I definitely think we need to do something about this, even if its just making some of these methods public so people can trigger update manually. Raising the priority.

All 42 comments

+1. Same issue I have after upgrading to 2.0.0-beta.10

Same issue after upgrading from 2.0.0-beta.8 to 2.0.0-beta.10

My workarround :

app.component.html

<md-sidenav class="main-sidenav" #sidenav mode="side" opened="false">

app.component.ts

export class AppComponent implements  AfterViewInit {

  @ViewChild('sidenav') 
  private sidenav: MdSidenav;

  ngAfterViewInit(): void {
    setTimeout(() => {
      this.sidenav.open();
    }, 250);
  }
}

that is exactly what i did @sbeaufort. It creates a small animation at the first page load but its ok

I have the exact same issue.

Here is my solution:
HTML

<md-sidenav-container #container>
    <md-sidenav  mode="side" opened="{{showMenu}}" class="shadow"></md-sidenav>
    <div class="my-content"></div>
</md-sidenav-container>

TS

export class MyComponent implements OnInit, AfterViewInit {
    @ViewChild('container') private _container;
    public showMenu: boolean = false;
    ...
    ngAfterViewInit() {
      setTimeout(()=>{
        this.showMenu = true;
      },0);
      this._container._ngZone.onMicrotaskEmpty.subscribe(() => {
        this._container._updateStyles();
        this._container._changeDetectorRef.markForCheck();
      })
    };
    ...
}

@ghwyf Thank you very much! This workaround also worked for me.

Same issue for me upgrading to 2.0.0-beta.10.
I used @ghwyf fix which works perfectly, thank you.

Closing as duplicate of #1130

Join the responses of @angularexample in #1130 and @ghwyf here, works for me.
_ngAfterViewInit_ seems too heavy for my application so I replace it with a specific call only when Subscription happens.

@josephperrott I think the issues are about different things. The one you referenced is about opening and closing the sidenav automatically when viewport width changes. This one is about being able to resize the sidenav width while keeping it open.

Before the last update this worked with no problem. Right now, the main content is not being resized when the sidebar width is resized. Some people have shared workarounds for this, but I think it would be nice if Angular Material would support this use case which is quite popular in places like firebase.

| Opened | Closed |
|-----|-----|
| image | image |

Reopening, sorry I misunderstood a bit there.

It would be really helpful to give a guide on this. Are we going to do this with animations? Will there be a function on the sidenav (like sidenav.resize(width))?

I am on the material2-builds and use the new fixedInViewport, which is great. But when I animate the sidenav from 220px to 75px the main content section does not adjust (as it used to before).

Any comments as to what we will expect here?

Same issue here.

After a while, I was forced to come back to 2.0.0-beta.8 when sidenav works perfect.

I would like to know if material team thinks this has to be fixed. If not, I will apply a workaround and stop waiting for a fix.

Thanks a lot!

Same problem here after upgrading to 2.0.0-beta.10 . The fix from @ghwyf doesn't work for me with angular 4.4.4

Here's my temporary workaround to fix it:

<md-sidenav-container #sideNavContainer>
  <md-sidenav #sideNav align="start" mode="{{navMode}}" opened="true"
    [@sideNavAnim] (@sideNavAnim.done)="onSideNavAnimDone($event)">
    <!-- sidenav content -->
  </md-sidenav>
  <!-- primary content -->
  <router-outlet *ngIf="sideNavAnimFinished"></router-outlet>
</md-sidenav-container>
export class MyComponent {
  navMode = 'side';
  sideNavAnimFinished = false;
  ...
  onSideNavAnimDone(event) {
    this.sideNavAnimFinished = true;
  }
}

the example mentioned by @sbeaufort does not work well when the component has changeDetection: ChangeDetectionStrategy.OnPush. In order to make it work you need to manually detect changes to the UI, example:

ngAfterViewInit(): void {
        const size = this.drawerState === 'open' ? 220 : 75;
        setTimeout(() => {
            this.sidenav.open();
            this.cd.detectChanges();
        }, size);
    }

@crisbeto is the resize function on a roadmap for sidenav?
Would it not suffice to add a "closeTo" option which, when in side mode and when set, does not transform to visibility hidden but sets the drawer width to whatever was closeTo was set to?

I was using @ghwyf workaround until beta.11 as well. What worked for me in beta.12 is the following:

export class MyComponent implements OnInit, AfterViewInit {
    @ViewChild('container') private _container;
    public showMenu: boolean = false;
    ...
    ngAfterViewInit() {
      setTimeout(()=>{
        this.showMenu = true;
      },0);
      this._container._ngZone.onMicrotaskEmpty.subscribe(() => {
        this._container._updateContentMargins();
        this._container._changeDetectorRef.markForCheck();
      })
    };
    ...
}

@mpschaeuble this does not seem to work anymore:
Property '_ngZone' is private and only accessible within class 'MatDrawerContainer'.

@mmalerba is there anything you guys can give us here?

I definitely think we need to do something about this, even if its just making some of these methods public so people can trigger update manually. Raising the priority.

wow. it's like every month something new breaks. im stuck inbetween versions no., have to let the sidenav go broken. please fix this soon.

Problem still present in 5.0.0-rc0

@mmalerba

I think exposing a method to reflow the sizenav is reasonable, most component with on push should be able to reflow based on a users request.

That said, it's not developer friendly and verbose... nothing serious since a drawer is not used frequently in an app but still requires work.
From what I see, in most of the use cases the developer will set the width value of a sidenav which is easy since it's a user defined dom element...
If we create an @Input for the width on sidenav we can automatically trigger the changes on the content element which is hidden from the developer.

This is automatic and does not require work from the dev.

The solution should be a public method + width input just in case

Working on a workaround I am getting more convinced that the MatDrawerContainer needs to be notified by MatDrawer when the MatDrawer width property is changed.

Just exposing _updateContentMargins() will not do.

Right now, calling _updateContentMargins() on MatDrawerContainer will query the width from the MatDrawer (left or right). The width is taken from the DOM element directly by querying the native DOM element offsetWidth property.

Simple animation transition in MatDrawer will make the whole thing fail.

For example, we have MatDrawer at 240px with 300ms transition and we change it to 60px and manually invoke _updateContentMargins().
When we call _updateContentMargins() the width is probably 240px or something a bit smaller.

Or a redux scenario where the width of MatDrawer is bound to an observable on the template, when do we call _updateContentMargins()?

Of course we can wait for animation to end but that is laggy, they should animate at the same time

Most workaround described above tackle the first render scenario

Same issue here with @angular/material": "5.0.0-rc0" with following settings

sidenavOpen: true,
sidenavMode: 'side',
sidenavCollapse: false,

image

@crisbeto thanks! Wonderful.

How does one achieve the minification of the sidenav now? Animations? Is there an example? Can I toggle to a certain width?

Thanks

@axtho what these changes do is allow you to opt in to having the drawer container resize once per change detection cycle, rather than on open/close.

@crisbeto so it is correct to assume that I would need to trigger change detection after running an animation when the component is on OnPush?

You _might_ not have to since the sidenav will check inside ngDoCheck which keeps firing not matter the change detection strategy.

Still happening in 6.4.7:

image

 "dependencies": {
    "@angular/animations": "6.1.8",
    "@angular/cdk": "^6.4.7",
    "@angular/common": "6.1.8",
    "@angular/compiler": "^6.1.8",
    "@angular/core": "6.1.8",
    "@angular/flex-layout": "^6.0.0-beta.18",
    "@angular/forms": "^6.1.8",
    "@angular/http": "6.1.8",
    "@angular/material": "^6.4.7",
    "@angular/platform-browser": "6.1.8",
    "@angular/platform-browser-dynamic": "6.1.8",
    "@angular/router": "6.1.8",
    "@angular/service-worker": "6.1.8",
    "@auth0/angular-jwt": "^2.0.0",
    "@ngx-formly/core": "^4.7.2",
    "@ngx-formly/material": "^4.7.2",
    "@ngx-translate/core": "^10.0.2",
    "@ngx-translate/http-loader": "^3.0.1",
    "core-js": "^2.5.2",
    "hammerjs": "^2.0.8",
    "material-design-icons-iconfont": "^3.0.3",
    "moment": "^2.22.1",
    "rxjs": "^6.3.2",
    "url": "^0.11.0",
    "util": "^0.11.0",
    "zone.js": "^0.8.26"
  },
  "devDependencies": {
    "@angular-devkit/build-angular": "^0.8.3",
    "@angular/cli": "^6.2.3",
    "@angular/compiler-cli": "^6.1.8",
    "@angular/language-service": "6.1.8",
    "@biesbjerg/ngx-translate-extract": "^2.3.1",
    "@ngx-rocket/scripts": "^2.1.0",
    "@types/jasmine": "^2.5.52",
    "@types/jasminewd2": "^2.0.2",
    "@types/lodash": "^4.14.103",
    "@types/node": "^8.9.4",
    "codelyzer": "^4.1.0",
    "htmlhint": "^0.10.1",
    "https-proxy-agent": "^2.0.0",
    "jasmine-core": "~2.99.1",
    "jasmine-spec-reporter": "^4.2.1",
    "karma": "^3.0.0",
    "karma-chrome-launcher": "^2.2.0",
    "karma-cli": "~1.0.1",
    "karma-coverage-istanbul-reporter": "^1.2.1",
    "karma-jasmine": "^1.1.0",
    "karma-jasmine-html-reporter": "^0.2.2",
    "karma-junit-reporter": "^1.2.0",
    "protractor": "^5.4.0",
    "stylelint": "~9.1.1",
    "stylelint-config-recommended-scss": "~3.1.0",
    "stylelint-config-standard": "~18.2.0",
    "stylelint-scss": "~2.5.0",
    "ts-node": "~5.0.0",
    "tslint": "~5.9.1",
    "typescript": "^2.9.2"
  }

Yep still an issue in 6.4.7
"@angular/material": "^6.4.7"

Come on, wheres the fix...?

Same. This is still an issue in 6.4.7

This solution is still a bit of a hack, but it's less of a hack than setTimeout...

@ViewChild('sidenavContainer') private sidenavContainer: MatSidenavContainer;

this.sidenavContainer._contentMarginChanges
    .pipe(
        debounceTime(250),
        tap(() => this.sidenavContainer._contentMargins.left = this.sidenav._width),
    )
    .subscribe();

this.sidenavContainer.autosize = true;

worked for me... after mucking with it for an hour. :(

I observed the same issue in 6.4.7, but only in certain cases:

  • when running the app in dev mode, there were no issues
  • when running the app in prod mode, the mat-sidenav-content was overlapped by the mat-sidenav
  • when disabling the buildOptimizer the issues disappeared
    When debugging the sourcecode of MatSidenavContainer, I recognized the code being executed to look like the source code of the 5.2.x branch
    (line 713): this._ngZone.run(() => this._contentMargins.next({left, right}));
    instead of
    (line 717): this._ngZone.run(() => this._contentMarginChanges.next(this._contentMargins));

Finally I identified the buildOptimizer cache as the culprit. After deleting <project>/node_modules/@angular-devkit/build-optimizer/src/.cache everything worked like a charm.

@dereekb this issue should not be closed.
Still exists in the year 2019.
@bobbydowling solution is the only thing worked for me. And I mucked with it for like 3 hours.

this.sidenavContainer.autosize = true;

worked for me... after mucking with it for an hour. :(

Where did you put autoSize = true ?
I have tried to put in OnInit() and AfterViewInit() but it didn't worked.

@MeghnathDas here is documentation of autosize flag. It is option of mat-drawer-container
https://material.angular.io/components/sidenav/api#MatDrawerContainer
HTH

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._

Was this page helpful?
0 / 5 - 0 ratings

Related issues

3mp3ri0r picture 3mp3ri0r  路  3Comments

vanor89 picture vanor89  路  3Comments

kara picture kara  路  3Comments

vitaly-t picture vitaly-t  路  3Comments

Miiekeee picture Miiekeee  路  3Comments