angular左側(cè)索引菜單(類似美團(tuán)左側(cè)菜單不分頁版本,點(diǎn)擊菜單滾動到對應(yīng)位置)

需求:

1、點(diǎn)擊左側(cè)分類菜單,右側(cè)界面滑動到對應(yīng)分類的第一個;
2、能嵌套在ng-Zorro的Drawer抽屜里面正常運(yùn)行;
3、某個分類的對應(yīng)內(nèi)容不存在了,分類菜單要有提示;

版本:

angular :7.1.0
ng-zorro-antd : 7.0.3

我這里分了兩個page,test是主體,test2只是Drawer抽屜,沒有做任何改動,直接上代碼吧

test.html


<div nz-row>

  <div nz-col nzSpan="6" class="box1">

    <ul>

      <li

        [ngClass]="{'active': active === i}"

        (click)="cl(item.value)"

        *ngFor="let item of list, let i = index"

        class="{{item.disabled?'disabled':''}}"

      >

        {{item.name}}

      </li>

    </ul>

  </div>

  <div

    nz-col

    nzSpan="18"

    class="box2"

    #box2

    id="aa"

    [ngStyle]="{height: height + 'px'}"

  >

    <ul>

      <li class="a" *ngFor="let item of a">{{item}}</li>

      <li class="b" *ngFor="let item of b">{{item}}</li>

      <li class="c" *ngFor="let item of c">{{item}}</li>

      <li class="d" *ngFor="let item of d">{{item}}</li>

    </ul>

  </div>

</div>

test.scss


ul {

  display: block;

  border: 1px solid #000;

  li {

    padding: 10px;

    border-bottom: 1px solid #000;

  }

}

.box1 {

  ul {

    li {

      &.disabled {

        background: #ccc;

        cursor: no-drop;

        opacity: 0.5;

      }

      &.active {

        background: #ccc;

      }

    }

  }

}

.box2 {

  height: 400px;

  overflow-y: scroll;

}

test.ts


import {

  Component,

  OnInit,

  ViewChild,

  ElementRef,

  AfterViewInit,

  ChangeDetectorRef

} from '@angular/core';

import { fromEvent } from 'rxjs';

import { debounceTime } from 'rxjs/operators';

import { reject } from 'q';

@Component({

  selector: 'app-test',

  templateUrl: './test.component.html',

  styleUrls: ['./test.component.scss']

})

export class TestComponent implements OnInit, AfterViewInit {

  a = [1, 1, 1, 1, 1, 1, 1, 1, , 1, 1, 1, 1, 1, 1];

  b = [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2];

  c = [3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3];

  d = [4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4];

  @ViewChild('box2') box2;

  active = 0;

  heightArr = [];

  //  左側(cè)菜單欄(需要手動配置的參數(shù),上下文關(guān)系name必須class保持一致)

  list = [

    {

      name: 'a',

      value: 0,

      disabled: false

    },

    {

      name: 'b',

      value: 0,

      disabled: false

    },

    {

      name: 'c',

      value: 0,

      disabled: false

    },

    {

      name: 'd',

      value: 0,

      disabled: false

    }

  ];

  //  55 是Drawer抽屜的title的高度,48 是Drawer抽屜的padding高度,根據(jù)實(shí)際情況自己配置

  height = window.innerHeight - 55 - 48;

  constructor(private el: ElementRef, private cdref: ChangeDetectorRef) {}

  ngOnInit() {}

  ngAfterViewInit() {

    const classNameArr = [];

    this.list.map(r => {

      classNameArr.push('.' + r.name);

    });

    classNameArr.map((r, i) => {

      if (!this.getFirstNode(r)) {

        this.list[i].disabled = true; //  當(dāng)頁面某個class不存在的時候設(shè)置不可操作狀態(tài)(用于某一個類型的數(shù)據(jù)不顯示的時候)

        this.cdref.detectChanges(); //  這個必須加,不加會報錯

      }

      this.list[i].value = this.getFirstNode(r);

      this.heightArr.push(this.getFirstNode(r));

    });

    this.scrollListen();

  }

  //  左側(cè)菜單點(diǎn)擊事件

  cl(n) {

    const nowScrollTop = this.box2.nativeElement;

    nowScrollTop.scrollTo({

      top: n,

      behavior: 'smooth'

    });

  }

  //  滾動監(jiān)聽

  scrollListen() {

    const nowScrollTop = this.box2.nativeElement;

    fromEvent(nowScrollTop, 'scroll')

      .pipe(debounceTime(30)) // 防抖 (如果你想要更加絲滑的效果直接吧防抖去掉吧)

      .subscribe(event => {

        const st = event['target'].scrollTop;

        this.active = this.scrollFindIndex(st, this.heightArr);

      });

  }

  //  獲取樣式組的第一個的offsetTop

  getFirstNode(className) {

    const arr = [].slice

      .call(document.querySelectorAll(className))

      .map(t => t.offsetTop);

    return arr[0];

  }

//  判斷值在數(shù)組的那個區(qū)域內(nèi)
  scrollFindIndex(val, arr) {
    let r;

    //  當(dāng)某一個class不存在的時候做出跳過處理
    arr.map((t, i) => {
      if (t === undefined || t === 0) {
        arr[i] = this.findNextNode(arr, i);
      }
    });
    console.log(arr);

    const n = arr.findIndex(el => el > val) - 1;
    switch (n) {
      case -1:
        r = 0;
        break;

      case -2:
        r = arr.length - 1;
        break;

      default:
        r = n;

        break;
    }
    return r;
  }

//    遞歸尋找下個有值的(我的具體情況最后一個是一定有值,其他情況自己改下吧)
  findNextNode(arr, i) {
    let node;
    if (arr[i]) {
      node = arr[i];
    } else {
      node = this.findNextNode(arr, i + 1);
    }
    return node;
  }

}

test2直接按文檔來就好了。

image

效果:


image.png

接下來我們模擬下其中一組數(shù)據(jù)沒有顯示,把其中一行代碼注釋了就好了


image.png
image.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容