【问题标题】:How to impliment a sidenav Navigation drawer with a (Mini variant)如何使用(迷你变体)实现 sidenav 导航抽屉
【发布时间】:2018-01-27 19:10:09
【问题描述】:

Angular Material 2 社区我需要你的帮助,如何在 Angular Material 2 中制作类似于谷歌材料设计示例的迷你变体。

我尝试实现这一点,但我无法做到这一点

到目前为止我的代码

  <!-- ===================================================================== -->
  <!-- SIDENAV && SIDENAV CONTAINER -->
  <!-- ===================================================================== -->
  <mat-sidenav-container>

    <mat-sidenav 
      #adminNavMenu 
      mode="side" 
      opened="true"
      style="min-width:50px; background: #F3F3F3;"
      class="shadow_right" autosize>

      <!-- MENU LEFT -->
      <app-admin-menu-left></app-admin-menu-left>

    </mat-sidenav>

   <mat-sidenav-container>

app-admin-menu-left.html

<mat-nav-list style="min-width:60px;">

  <mat-list-item *ngFor="let page of Menus"> 
    <a routerLink="{{page.link}}" routerLinkActive="active" 
      [routerLinkActiveOptions]="{exact:true}" matLine>
      <mat-icon class="home_icon collapse-icon vcenter" mat-list-icon>{{page.icon}}</mat-icon>
      <span *ngIf="!showFiller">
        {{page.name}}
      </span>
    </a>
  </mat-list-item>


</mat-nav-list>

<button  mat-icon-button (click)="showFiller = !showFiller" mat-raised-button>
  <mat-icon *ngIf="!showFiller">chevron_right</mat-icon>
  <mat-icon *ngIf="showFiller">chevron_left</mat-icon> 
</button>

并给了我这个意想不到的结果

点击查看迷你吧后

如您所见,mat-sidenav-content 上有 250 像素的边距,但我无法访问此元素。

任何解决此问题的帮助都会很有用。

谢谢

【问题讨论】:

标签: angular angular-material2


【解决方案1】:

我已经用那个解决方案解决了这个问题。

祝你好运。

另外,我在Stackblitz做了一个工作示例

home.component.html

    <div>
    
      <mat-sidenav-container>
    
        <mat-sidenav #adminNavMenu mode="side" opened="true" style="min-width:60px; background: #F3F3F3;" class="shadow_right" autosize>
    
          <!-- MENU LEFT -->
          <app-admin-menu-left></app-admin-menu-left>
    
        </mat-sidenav>
    
        <!-- ================================================================= -->
        <!-- ************************* MAIN CONTAINER ************************ -->
        <!-- ================================================================= -->
        <mat-sidenav-content [@onSideNavChange]="sideNavState">
          <div class="main_container" fxLayout="column" fxLayoutGap="0px" style="height:100vh;">
    
            <!-- =============================================================== -->
            <!-- Your main content -->
            <!-- =============================================================== -->
    
          </div>
        </mat-sidenav-content>
    
      </mat-sidenav-container>
    
    
    </div>

home.component.ts


    import { Component, OnInit } from '@angular/core';
    import { MatSidenav } from '@angular/material';
    import { trigger, state, style, transition, animate } from '@angular/animations';
    
    import { MediatorService } from '@app-services/mediator/mediator.service';
    
    @Component({
      selector: 'app-admin-analytics',
      templateUrl: './admin-analytics.component.html',
      styleUrls: ['./admin-analytics.component.css'],
      animations: [
        trigger('onSideNavChange', [
          state('close',
            style({
              'margin-left': '60px'
            })
          ),
          state('open',
            style({
              'margin-left': '250px'
            })
          ),
          transition('close => open', animate('250ms ease-in')),
          transition('open => close', animate('250ms ease-in')),
        ]),
    
        trigger('onPageReady', [
          state('inactive',
            style({
              opacity: 0.4
            })
          ),
          state('active',
            style({
              opacity: 1
            })
          ),
          transition('inactive => active', animate('250ms ease-in')),
          transition('active => inactive', animate('250ms ease-in')),
        ])
      ]
    })
    export class HomeComponent implements OnInit {
    
    
      /**
       * Get the sidenav state.
       */
      sideNavState: string = this.mediator.getSideNavState;
    
 
     
      constructor(
        private mediator: MediatorService,
      ) { }
    
      ngOnInit() {
    
    
        // Subscribe on changes important.
        this.mediator.sideNavListener.subscribe( state => {
          this.sideNavState = state;
        });
    
      }
    
    
    }

mediator.service.ts


    import { Injectable } from '@angular/core';
    import { Subject } from 'rxjs/Subject';
    
    
    @Injectable()
    export class MediatorService {
    
      APP_VERSION: String = 'v8.3.1.36';
    
      // default value.
      // this variable track the value between sessions.
      private _sideState: any = 'open';
    
      /** This is the mini variant solution with animations trick. */
      sideNavListener: any = new Subject();
    
      get sideNavState() {
        return this._sideState;
      }
    
      setSidenavState(state) {
        this._sideState = state;
      }
    
    
      constructor() {
    
        this.sideNavListener.subscribe( state => {
          this.setSidenavState(state);
        });
      
    
      }
    
    
    }

menu-left.component.html


    <div class="sidenav_menu_left" 
        [@onSideNavChange]="sideNavState" 
        style="width:100%; height: 100vh;" 
        fxLayout="column" 
        [style.overflow]="overflowState">
    
        <p>Sidenav content left</p>
    
        <!-- this can toggle the sidenav -->
        <div fxFlex="100" (click)="toggleSideNav();" class="hoverble"></div>
    
    </div>

menu-left.component.ts


    import { Component, OnInit, Input } from '@angular/core';
    import { MatSidenav } from '@angular/material';
    import {trigger, state, style, transition, animate, keyframes, query, group} from '@angular/animations';
    
    
    // Mediator: the main service, later this service is gonna have more generic use.
    import { MediatorService } from '@app-services/mediator/mediator.service';
    import { delay } from 'q';
    
    
    @Component({
      selector: 'app-admin-menu-left',
      templateUrl: './admin-menu-left.component.html',
      styleUrls: ['./admin-menu-left.component.css'],
      animations: [
    
        // animate sidenave
        trigger('onSideNavChange', [
          state('close',
            style({
              width: '60px'
            })
          ),
          state('open',
            style({
              width: '250px'
            })
          ),
          transition('close => open', animate('250ms ease-in')),
          transition('open => close', animate('250ms ease-in')),
    
        ])
    
      ]
    })
    export class MenuLeftComponent implements OnInit {
    
    
      /**
       * Get the sidenav state,
       */
      sideNavState: string = this.mediator.sideNavState;
    
    
      overflowState: any = 'auto';
    
      constructor(
        private mediator: MediatorService
      ) {
    
      }
    
      ngOnInit() {
    
        this.mediator.sideNavListener.subscribe( state => {
          this.sideNavState = state;
        });
    
      }
    
      /**
       * On animation done.
       * @param x
       */
      animationEvent(x) {
        this.overflowState = 'auto';
      }
    
      /**
       * Toggle the sidenave state.
       *
       * Hides entire sidenav onclose.
       */
      setSideNavState() {
        this.mediator.toggle().then( snap => {
          console.log(snap);
        });
      }
    
    
      /**
       * Toggle, Open or close the sidenav.
       *
       * Set the sidenave state on mediator.
       */
      toggleSideNav() {
    
        switch (this.sideNavState) {
    
          case 'close':
    
            this.sideNavState = 'open';
            this.mediator.setSideNavState(this.sideNavState);
    
            setTimeout( () => {{
              this.sideNavText      = this.sideNavText === 'open' ? 'close' : 'open';
              this.sideNavIcon      = this.sideNavIcon === 'open' ? 'close' : 'open';
              this.sideNavCopyRight = this.sideNavCopyRight === 'open' ? 'close' : 'open';
            }}, 200);
          break;
    
          case 'open':
            this.sideNavText      = this.sideNavText === 'open' ? 'close' : 'open';
            this.sideNavIcon      = this.sideNavIcon === 'open' ? 'close' : 'open';
            this.sideNavCopyRight = this.sideNavCopyRight === 'open' ? 'close' : 'open';
    
            setTimeout( () => {{
              this.sideNavState = this.sideNavState === 'open' ? 'close' : 'open';
              this.mediator.setSideNavState(this.sideNavState);
            }}, 200);
          break;
    
          default:
            console.log('#6644');
            break;
        }
    
        this.overflowState = 'hidden';
      }
    
    }

【讨论】:

  • 我正在尝试应用您的解决方案,但我发现存在一些无法识别的变量的问题。你有一个工作的例子吗?它也给我这个错误:提供的动画触发器“onSideNavChangeC”尚未注册!
  • 你必须提供正确的动画名称再看home.component.ts
  • 将“onSideNavChangeC”重命名为onSideNavChange
  • sideNavState: any = this.mediator.sideNavState;它用 MenuLeftComponent 编写,但中介服务中没有“sideNavState”。 this.mediator.toggle().then( snap => { console.log(snap); });而“toggle()”也不在那里
  • @Rahimjon Rustamov 我今天会更新答案,但在更新之前,我认为您缺少一些步骤。
【解决方案2】:

我已经为此苦苦挣扎。解决方案比您想象的要简单得多……几乎所有 Angular 中的东西都可以动画,我们可以用几行非常简单的动画代码来解决这个问题……

在名为 sidenav.animations.ts 的文件中 您将创建一个动画来为 的宽度在 200px 和 60px 之间设置动画

您将创建第二个动画,以使 的左侧边距介于 201 像素和 61 像素之间。

./sidenav.animations.ts

 import {
  animate,
  state,
  style,
  transition,
  trigger,
} from '@angular/animations';

/*
 * animation: sideNaveAnimation
 * trigger: 'openClose'
 *
 * comments: sets the width of an element to 200px when 'open' and to 60px
 *   when closed.  Animates in between these two states over '0.3s'
 */

export const sideNavAnimation = trigger('openCloseSidenav', [
    // ...
    state('open', style({
      width: '200px',
    })),
    state('closed', style({
      width: '60px',
    })),
    transition('open <=> closed', [
      animate('0.3s')
    ]),
  ]);

/*
 * animation: sideNavContainerAnimation
 * trigger: 'openCloseSidenavContent'
 *
 * comments: Sets the margin-left to 201px when "open" and 61px when "closed".
 */

export const sideNavContainerAnimation = trigger('openCloseSidenavContent', [
    state('open', style({
      'margin-left': '201px',
    })),
    state('closed', style({
      'margin-left': '61px',
    })),
    transition('open <=> closed', [
      animate('0.3s')
    ]),
  ]);

在你的 app.component.ts...

您将导入两个动画并在动画数组中使用它们,这将允许您在 app.component.html 中使用 sidenav.animations.ts(“openCloseSidenav”和“openCloseSidenavContent”)中定义的触发器

您将创建一个布尔值 (isOpen) 来跟踪 sidenav 所处的状态

您将创建一个名为 toggle() 的函数,它将在 true 和 false 之间切换变量 isOpen。该函数将通过按下 app.component.html 中的按钮调用

./app.component.ts 不要忘记将您的 MatSidenavModule、MatButtonsModule、MatIconModule、MatListModule 和 BrowserAnimationsModule 也导入到您的 app.module.ts 中,否则您将无法在 app.component 中的任何位置使用 或其中的任何内容.html

import { Component } from '@angular/core';

//...

import { sideNavAnimation, sideNavContainerAnimation } from './sidenav.animations';

//...


@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss'],
  animations: [sideNavAnimation, sideNavContainerAnimation]
})

export class AppComponent {

  isOpen = true;

  toggle() {
     this.isOpen = !this.isOpen;
   }

//...

}

在你的 app.component.html... 您将使用放置在 sidenav 某处的按钮调用 toggle()。

您将在 上放置动画触发器:'openCloseSidenav',并带有一个 表达式,该表达式将选择两个已定义状态中的哪一个(“打开”或“关闭”,如sidenav.animations.ts) 元素应该处于基于变量 isOpen 的状态并在状态之间进行动画处理。

您将使用与上述相同的表达式将动画触发器:'openCloseSidenavContent' 放置在 上。

./app.component.html

<mat-sidenav-container>

  <mat-sidenav [@openCloseSidenav]="isOpen ? 'open' : 'closed'" mode="side" opened role="navigation">
    <mat-nav-list>
      <!-- Place nav links here -->

      <button type="button" aria-label="Toggle nav" mat-icon-button (click)="toggle()">
      <mat-icon aria-label="Side nav toggle icon">menu</mat-icon>
      </button>

    </mat-nav-list>
  </mat-sidenav>

  <mat-sidenav-content [@openCloseSidenavContent]="isOpen ? 'open' : 'closed'">
    <router-outlet></router-outlet>
  </mat-sidenav-content>

</mat-sidenav-container>

剩下的你自己填,我的里面有如下图。

...我正在使用变量 isOpen 来决定是否应显示图标旁边的文本..我很快会在 标签中添加动画以使这些动画在toggle() 被调用并且 isOpen 发生变化,否则我会将动画时间缩短到 0.0 秒,以便它在两种状态之间立即捕捉...

...然而,即使标签中没有 *ngIf="isOpen" 也可以正常工作...

    <a mat-list-item (click)="signOut()">
        <span  class="app-nav-list-icon">
          <mat-icon matListIcon class="app-nav-list-icon">
            <fa-icon icon="sign-out-alt"style="color: #808DE1;"></fa-icon>
          </mat-icon>
        </span>
        <mat-chip-list *ngIf="isOpen" style="padding-left: .5em;">
          <mat-chip>Logout</mat-chip>
        </mat-chip-list>
      </a>

...这里的重点是 简单 动画可用于控制应用程序各个方面的行为和外观...

请访问 https://angular.io/guide/animations 了解有关其工作原理的更多详细信息...

【讨论】:

    【解决方案3】:

    您必须在标题中放入属性“autosize”

     &lt;mat-sidenav-container autosize &gt;

    使用此属性,内容可以调整为菜单的宽度

    【讨论】:

    • nop 不起作用我试过了。这个问题很老了。我用角度动画解决了这个问题。感谢您的帮助:)
    • @George 能否提供您的动画解决方案?
    • @LarsGyrupBrinkNielsen 让我知道这是否对您有帮助。我添加了我的解决方案
    【解决方案4】:

    使用打字稿代码,您可以像这样更新侧边栏的宽度:

    toggleSideBar(){
        // console.log('toggle called');
        // this.sb.toggle();
        if(this.widthSideBar == this.widthSideBarExpanded){
          this.widthSideBar = this.widthSideBarCollapsed;
        }
        else{
          this.widthSideBar = this.widthSideBarExpanded;
        }
        // snav.toggle();
      }
    

    对应的html文件会是这样的

    ..
    <mat-sidenav [style.width.px]="widthSideBar">..</mat-sidenav>
    ..
    <mat-sidenav-content [style.marginLeft.px]="widthSideBar">..</mat-sidenav-content>
    ..
    

    这里我设置了内容的宽度和左边距动态调整。

    【讨论】:

      【解决方案5】:

      Angular Material 追踪的原图: https://github.com/angular/components/issues/1728

      材料团队最近发布了此行为的规范: https://material.io/components/navigation-rail#behavior

      名为 Jordan Hall 的人已经发布了一项指令,将为您处理此问题: https://www.npmjs.com/package/angular-material-rail-drawer 在这里回购https://github.com/Jordan-Hall/angular-material-rail-drawer-plugin

      我即将在我自己的项目中使用 Jordans 包。如果有人问,我们会更新反馈。

      【讨论】:

      • Greg 如果您遇到任何问题,请告诉我。这个周末我正在查看悬而未决的问题。
      • 工作愉快!我目前在基本场景中使用它,媒体查询模式没有属性绑定,随着网站的成熟,这将在稍后出现。我还没有特别关注动画、样式和跨浏览器兼容性,因为我还是 pre-alpha。开始在 Angular 9.1.5 中使用它,并进行了 --forced 升级到 Angular Material 10,仍然有效。
      • 为该反馈干杯。我的目标是尽快发布发布版本。但是,我也开始着手将其纳入主仓库github.com/angular/components/pull/20050
      • Angular 12 似乎不支持此功能。(请参阅)[github.com/Jordan-Hall/angular-material-rail-drawer-plugin/…
      猜你喜欢
      • 2021-06-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-11-03
      • 2020-07-11
      • 2016-11-15
      • 1970-01-01
      相关资源
      最近更新 更多