it-swarm.dev

Hover üzerindeki Angular mat menüsünü açma ve kapatma

Bu soru, this Github konusuna atıfta bulunur, fare vurgulu kullanılarak değiştirilemeyen mat-menu ile temel olarak açısal malzeme menüsüyle önyükleme tabanlı yatay gezinme menüsünü değiştirmeye çalışıyorum. Beni bootstrap tabanlı menüyü çoğaltmaktan alıkoyan tek şey vurguda mat-menu açmak ve kapatmak ... __ Yukarıdaki Github sayısında da belirtildiği gibi, mouseEnter kullanmak gibi istediğim şeyi elde etmek için bazı geçici çözümler var

(mouseenter)="menuTrigger.openMenu()"

veya mat-menu close'i bağlamak için Mat-menu içine span içine ekleme,

<mat-menu #menu="matMenu" overlapTrigger="false">
  <span (mouseleave)="menuTrigger.closeMenu()">
    <button mat-menu-item>Item 1</button>
    <button mat-menu-item>Item 2</button>
  </span>
</mat-menu>

ancak çözümlerin hiçbiri her küçük senaryoyu kapsamıyor gibi görünmüyor,

Örneğin.

Yukarıdaki Github sayısında da belirtildiği gibi, ilk SO çözümünde aşağıdaki sorunlar var.

  • Fare imlecini düğmenin üzerine getirin; menü açılır. Ancak düğmeye tıklarsanız, menüyü gizler ve gösterir. BENİM NACİZANE FİKRİME GÖRE bu bir böcek.
  • Menüyü gizlemek için kullanıcının menünün dışını tıklaması gerekir. İdeal olarak, fare imleci dışarıdaysa menü gizlenir
    Alanın (düğmeyi, menüyü ve alt menüleri içeren)
    400ms'den daha uzun.

Ve yukarıdaki sorunlardan birini çözmeye çalışan, ancak gerektiği gibi çalışmayan bir çözümde, örneğin;.

MatMenuTrigger üzerine gelmek, mat-menu işlevini beklendiği gibi açar ancak bir kullanıcı fareyi mat-menu girmeden uzaklaştırırsa, otomatik olarak kapanmaz, hangisi yanlışsa.

Ayrıca iki alt menü seviyesinden birine geçmek de istediğim olmayan birinci seviye menüyü kapatıyor.

P. fareyi bir açılan menüden bir sonraki kardeşine hareket ettirmek, diğerini açmaz. Bunun belirtildiği şekilde başarılması zor olabilir sanırım burada , Ama bunlardan bazılarının elde edilebileceği doğru olabilir mi?

İşte temel stackBlitz olanlarımı tekrar üretiyor, herhangi bir yardım için teşekkür ediyorum.

5
Saif Ullah

İlk zorluk, mat-menu, CDK kaplaması kaplamanın z-index'si nedeniyle oluşturulduğunda odağı butondan çalıyor ... bunu çözmek için z-endeksini düğme için bir stilde ayarlamanız gerekiyor ... 

  • Bu, düğmeye bir (mouseleave) eklediğinizde özyinelemeli döngüyü durduracaktır. style="z-index:1050"

Daha sonra levelone ve levelTwo menüleri için tüm enter ve leave olaylarını izlemeniz ve bu durumu iki bileşen değişkeninde saklamanız gerekir.

enteredButton = false;
isMatMenuOpen = false;
isMatMenu2Open = false;

Daha sonra menü girişi ve menuLeave her iki menü seviyesi için metodlar yaratır .. not menuLeave(trigger), level2'ye erişilip erişilmediğini kontrol eder ve doğruysa hiçbir şey yapmaz.

Lütfen Dikkat:menu2Leave(), navigasyonun birinci seviyeye geri dönmesine izin vermek için mantığa sahiptir ancak diğer taraftan çıkıldığında her ikisini de kapatır ... ayrıca seviye izninde düğme odağını kaldırır.

menuenter() {
    this.isMatMenuOpen = true;
    if (this.isMatMenu2Open) {
      this.isMatMenu2Open = false;
    }
  }

  menuLeave(trigger, button) {
    setTimeout(() => {
      if (!this.isMatMenu2Open && !this.enteredButton) {
        this.isMatMenuOpen = false;
        trigger.closeMenu();
        this.ren.removeClass(button['_elementRef'].nativeElement, 'cdk-focused');
        this.ren.removeClass(button['_elementRef'].nativeElement, 'cdk-program-focused');
      } else {
        this.isMatMenuOpen = false;
      }
    }, 80)
  }

  menu2enter() {
    this.isMatMenu2Open = true;
  }

  menu2Leave(trigger1, trigger2, button) {
    setTimeout(() => {
      if (this.isMatMenu2Open) {
        trigger1.closeMenu();
        this.isMatMenuOpen = false;
        this.isMatMenu2Open = false;
        this.enteredButton = false;
        this.ren.removeClass(button['_elementRef'].nativeElement, 'cdk-focused');
        this.ren.removeClass(button['_elementRef'].nativeElement, 'cdk-program-focused');
      } else {
        this.isMatMenu2Open = false;
        trigger2.closeMenu();
      }
    }, 100)
  }

  buttonEnter(trigger) {
    setTimeout(() => {
      if(this.prevButtonTrigger && this.prevButtonTrigger != trigger){
        this.prevButtonTrigger.closeMenu();
        this.prevButtonTrigger = trigger;
        trigger.openMenu();
      }
      else if (!this.isMatMenuOpen) {
        this.enteredButton = true;
        this.prevButtonTrigger = trigger
        trigger.openMenu()
      }
      else {
        this.enteredButton = true;
        this.prevButtonTrigger = trigger
      }
    })
  }

  buttonLeave(trigger, button) {
    setTimeout(() => {
      if (this.enteredButton && !this.isMatMenuOpen) {
        trigger.closeMenu();
        this.ren.removeClass(button['_elementRef'].nativeElement, 'cdk-focused');
        this.ren.removeClass(button['_elementRef'].nativeElement, 'cdk-program-focused');
      } if (!this.isMatMenuOpen) {
        trigger.closeMenu();
        this.ren.removeClass(button['_elementRef'].nativeElement, 'cdk-focused');
        this.ren.removeClass(button['_elementRef'].nativeElement, 'cdk-program-focused');
      } else {
        this.enteredButton = false;
      }
    }, 100)
  }

HTML

aşağıda tüm bunları nasıl bağlayacağınız açıklanmaktadır.

<ng-container *ngFor="let menuItem of modulesList">

    <ng-container *ngIf="!menuItem.children">
        <a class="nav-link">
            <span class="icon fa" [ngClass]="menuItem.icon"></span>
      <span class="text-holder">{{menuItem.label}}</span>
    </a>
  </ng-container>
  <ng-container *ngIf="menuItem.children.length > 0">
    <button #button mat-button [matMenuTriggerFor]="levelOne" #levelOneTrigger="matMenuTrigger" (mouseenter)="levelOneTrigger.openMenu()" (mouseleave)="buttonLeave(levelOneTrigger, button)" style="z-index:1050">
      <span class="icon fa" [ngClass]="menuItem.icon"></span>
      <span>{{menuItem.label}}
        <i class="fa fa-chevron-down"></i>
      </span>
    </button>

    <mat-menu #levelOne="matMenu" direction="down" yPosition="below">
      <span (mouseenter)="menuenter()" (mouseleave)="menuLeave(levelOneTrigger, button)">
      <ng-container *ngFor="let childL1 of menuItem.children">
        <li class="p-0" *ngIf="!childL1.children" mat-menu-item>
          <a class="nav-link">{{childL1.label}}
            <i *ngIf="childL1.icon" [ngClass]="childL1.icon"></i>
          </a>
        </li>
        <ng-container *ngIf="childL1.children && childL1.children.length > 0">
          <li mat-menu-item #levelTwoTrigger="matMenuTrigger" [matMenuTriggerFor]="levelTwo">
            <span class="icon fa" [ngClass]="childL1.icon"></span>
            <span>{{childL1.label}}</span>
          </li>

          <mat-menu #levelTwo="matMenu">
            <span (mouseenter)="menu2enter()" (mouseleave)="menu2Leave(levelOneTrigger,levelTwoTrigger, button)">
            <ng-container *ngFor="let childL2 of childL1.children">
              <li class="p-0" mat-menu-item>
                <a class="nav-link">{{childL2.label}}
                  <i *ngIf="childL2.icon" [ngClass]="childL2.icon"></i>
                </a>
              </li>
            </ng-container>
            </span>
          </mat-menu>
        </ng-container>
      </ng-container>
      </span>
    </mat-menu>
  </ng-container>

</ng-container>

Stackblitz

https://stackblitz.com/edit/mat-nested-menu-yclrmd?embed=1&file=app/nested-menu-example.html

8
Marshal

Bu çözüm Z-endeksi ayarına bir alternatif olarak kullanılabilir: Mareşal'in önerdiği gibi 1050. Diğer düzeltmeler için Mareşal'in cevabını kontrol etmelisiniz.

Kullanabilirsiniz 

<button [matMenuTriggerFor]="menu" #trigger="matMenuTrigger" (mouseenter)="trigger.openMenu()" (mouseleave)="trigger.closeMenu()"></button>

Bunu kullanmak, sürekli titreşimsiz döngü yaratacaktır, ancak basit bir düzeltme vardır.

Yani, tek bir şeyin dikkat edilmesi gereken:

menü açıldığında

<div class="cdk-overlay-container"></div>

bu div, genellikle/body etiketinden hemen önce bütün html'nin sonuna eklenen tüm ekranı kaplar. Tüm menüleriniz bu kabın içinde oluşturulur. (sınıf adı farklı sürümlerde farklı olabilir).

Bunu css/scss stiller dosyasına ekleyin:

.cdk-overlay-container{
    left:200px;
    top:200px;
}
.cdk-overlay-connected-position-bounding-box{
    top:0 !important;

}

veya bu öğenin düğmenizle üst üste gelmesini durduran herhangi bir şey.

Bunu kendim denedim, cevabımın net ve kesin olduğunu umuyorum.

İşte stackblitz demosunun aynısı, sorudaki stackblitz kodunu değiştirdim.

0
Sunil Kumar