【问题标题】:Swipe to delete in Angular 10在 Angular 10 中滑动删除
【发布时间】:2021-08-04 16:26:57
【问题描述】:

您好,我的开发伙伴。我只想寻求帮助,以在我们的 Angular 项目中实现滑动删除功能。

请看下面的截图。

这是提供给我使用该功能的代码。

      <div class="col-md-6 col-sm-6 col-xs-12" *ngFor="let relation of bookList" >
        <div class="" (click)="selectBookFor(relation)">
          <div class="">
            <div class="row introMain-wrapper">
              <div class="col-auto">
                <div class="introImg">
                  <img src="{{ relation && relation.image }}"  alt="user" (error)="setDefaultRelationUserPic($event, relation?.gender)">
                </div>
              </div>
              <div class="col">
                <div class="row">
                  <div class="col"><h5 class="card-title text">{{relation?.firstName}} {{relation?.lastName}}</h5></div>
                  <div class="col-auto"><span class="relation-text">{{relation?.relationship}}</span></div>
                </div>
                <h5 class="card-text">{{relation?.email}}</h5>
                <div class="outerdiv-text">
                    <span class="card-text">{{relation?.phone1}}</span>
                </div>
              </div>
            </div>
            <div class="line"></div>
          </div>
        </div>
      </div>

我试过以下插件:

  • mat-list-touch
  • 滑动角度列表

但在子模块中导入并在组件功能中使用时将不起作用。提前致谢。

【问题讨论】:

    标签: html angular angular-material scss-lint


    【解决方案1】:

    可以使用Angular材质拖放https://material.angular.io/cdk/drag-drop/examples

    由此,您可以创建滑动删除选项。

    演示

    <div class="container">
      <div class="row">
        <div
          cdkDropList
          cdkDropListSortingDisabled
          [cdkDropListData]="bookList"
          class="example-list"
          (cdkDropListDropped)="drop($event)">
          <div class="example-box" *ngFor="let item of bookList;let i= index" cdkDrag>{{item.firstName}}</div>
        </div>
      </div>
    </div>
    

    TS

    drop(event: CdkDragDrop<any, any>): any {
        this.bookList.splice(event.currentIndex, 1);
    }
    

    【讨论】:

    • 不幸的是,我在使用这种方法时遇到了导入问题。
    • 这里是 ./src/styles.scss 中的错误 ERROR (./node_modules/css-loader/dist/cjs.js??ref--14-1!./node_modules/postcss- loader/src??embedded!./node_modules/resolve-url-loader??ref--14-3!./node_modules/sass-loader/dist/cjs.js??ref--14-4!./src /styles.scss)模块构建失败(来自 ./node_modules/sass-loader/dist/cjs.js):SassError:找不到要导入的样式表。 ╷ 4 │ @import '~@angular/material'; │ ^^^^^^^^^^^^^^^^^^^ ╵ 我试过这个stackoverflow.com/questions/67652012/… 但不行
    • 你确定,你正确安装了角材料
    • 不幸的是,是的:(
    【解决方案2】:

    angular 支持事件 touchstart、touchend 和 touchmove。所以你可以想象这样一个指令:

    export interface TouchEventType {
      element: TouchDirective;
      incrX: number;
      incrY: number;
    }
    
    @Directive({
      selector: '[touch]',
      exportAs: 'touch'
    })
    export class TouchDirective {
      origin: any = { x: 0, y: 0 };
      style: any = null;
      rect: any = { x: 0, y: 0 };
      incrX: number = 0;
      incrY: number = 0;
      @Input('touch') direction: 'horizontal' | 'vertical' | null = null;
      @Output() touchMove: EventEmitter<any> = new EventEmitter<any>();
      @HostListener('touchstart', ['$event']) touchStart(event) {
        this.origin = {
          x: event.touches[0].screenX,
          y: event.touches[0].screenY
        };
      }
    
      @HostListener('touchmove', ['$event']) touch(event: TouchEvent) {
        this.incrX = this.rect.x + event.touches[0].screenX - this.origin.x;
        this.incrY = this.rect.y + event.touches[0].screenY - this.origin.y;
        this.style =
          this.direction == 'horizontal'
            ? {
                transform: 'translateX(' + this.incrX + 'px)'
              }
            : this.direction == 'vertical'
            ? {
                transform: 'translateY(' + this.incrY + 'px)'
              }
            : {
                transform: 'translateY(' + this.incrX + 'px,' + this.incrY + 'px)'
              };
    
          if (this.direction)
              window.scrollBy(this.direction=='horizontal'?
                    event.touches[0].screenX - this.origin.x:0,
              this.direction=='vertical'?
                    event.touches[0].screenY - this.origin.y:0)
    
      }
    
      @HostListener('touchend', ['$event']) touchEnd() {
        this.rect = { y: this.incrY, x: this.incrX };
        this.touchMove.emit({
          element: this,
          incrX: this.incrX,
          incrY: this.incrY
        });
      }
      @HostBinding('style') get _() {
        return this.style;
      }
      constructor(private elementRef: ElementRef) {}
    
      reset() {
        this.style = null;
        this.rect = { x: 0, y: 0 };
      }
    }
    

    你可以在.html中使用

    <ng-container *ngFor="let item of array;let i=index">
      <p touch='horizontal' (touchMove)="touchmove($event,i)">
      Start editing to see some magic happen {{item}}
      </p>
    </ng-container>
    
      array:any[]=[1,2,3,4,5,6,7]
    
      touchmove(event:TouchEventType,index:number)
      {
        if (event.incrX<-10) //10px to the left
          this.array.splice(index,1)
        else
          event.element.reset()
      }
    

    查看stackblitz,不提供任何担保

    更新如果我们希望这项工作在触摸屏和非触摸屏中,我们可以采取另一种方法。我们可以使用fromEvent rxjs 运算符来代替使用@HostListener。 (否则我们需要在 mousedown、mouseup 和 mousemove 上创建一个 hostListenr)

    为了控制触摸事件和鼠标事件,我们使用 rxjs 合并操作符,它从两个 observable 接收值。为了使用完全相同的代码,我们在触摸事件中使用“map”来转换 MouseEvent 中的响应(一个 TouchEvent)。

    最后一点是使用“点击”来判断是否是触摸屏。在触摸屏中,如果我们向下/向上滑动,我们需要滚动窗口 dwon/up 是我们的指令只允许水平移动

    @Directive({
      selector: '[touch]',
      exportAs: 'touch'
    })
    export class TouchDirective {
      origin: any = { x: 0, y: 0 };
      style: any = null;
      rect: any = { x: 0, y: 0 };
      incrX: number = 0;
      incrY: number = 0;
    
      onDrag:boolean=false;    
      isTouched:boolean=false;
      moveSubscription: any;
      downSubscription: any;
    
      @Input('touch') direction: 'horizontal' | 'vertical' | null = null;
      @Output() touchMove: EventEmitter<any> = new EventEmitter<any>();
    
      @HostBinding('style') get _() {
        return this.style;
      }
      @HostBinding('class.no-select') get __() {
        return this.onDrag;
      }
    
      constructor(private elementRef: ElementRef) {}
      ngOnInit() {
        this.downSubscription=merge(
          fromEvent(this.elementRef.nativeElement, 'mousedown').pipe(tap(_=>this.isTouched=false)),
          fromEvent(this.elementRef.nativeElement, 'touchstart').pipe(tap(_=>this.isTouched=true),
            map((event: TouchEvent) => ({
              target: event.target,
              screenX: event.touches[0].screenX,
              screenY: event.touches[0].screenY
            }))
          )
        ).subscribe((event: MouseEvent) => {
    
          //see that is the same code that @HostListener('touchstart', ['$event'])
          this.origin = {
            x: event.screenX,
            y: event.screenY
          };
          this.onDrag=true;
    
          merge(fromEvent(document, 'mouseup'), fromEvent(document, 'touchend'))
            .pipe(take(1))
            .subscribe(() => {
          //see that is the same code that @HostListener('touchend', ['$event'])
              if (this.moveSubscription) {
                this.moveSubscription.unsubscribe();
                this.moveSubscription = undefined;
              }
              this.rect = { y: this.incrY, x: this.incrX };
              this.touchMove.emit({
                element: this,
                incrX: this.incrX,
                incrY: this.incrY
              });
              this.onDrag=false;
            });
    
          if (!this.moveSubscription) {
            this.moveSubscription = merge(
              fromEvent(document, 'mousemove'),
              fromEvent(document, 'touchmove').pipe(
                map((event: TouchEvent) => ({
                  target: event.target,
                  screenX: event.touches[0].screenX,
                  screenY: event.touches[0].screenY
                }))
                ))
                .subscribe((event: MouseEvent) => {
          //see that is the same code that @HostListener('touchmove', ['$event'])
    
                  this.incrX = this.rect.x + event.screenX - this.origin.x;
                  this.incrY = this.rect.y + event.screenY - this.origin.y;
                  this.style =
                    this.direction == 'horizontal'
                      ? {
                          transform: 'translateX(' + this.incrX + 'px)'
                        }
                      : this.direction == 'vertical'
                      ? {
                          transform: 'translateY(' + this.incrY + 'px)'
                        }
                      : {
                          transform: 'translateY(' + this.incrX + 'px,' + this.incrY + 'px)'
                        };
                  if (this.direction && this.isTouched)
                    window.scrollBy(
                      this.direction == 'horizontal'
                        ? event.screenX - this.origin.x
                        : 0,
                      this.direction == 'vertical'
                        ? event.screenY - this.origin.y
                        : 0
                    );
                })
          }
        });
    
      }
      ngOnDestroy() {
        this.downSubscription.unsubscribe();
      }
      reset() {
        this.style = null;
        this.rect = { x: 0, y: 0 };
      }
    }
    

    我们添加类

    .no-select {
        -webkit-touch-callout: none; /* iOS Safari */
        -webkit-user-select: none; /* Safari */
        -khtml-user-select: none; /* Konqueror HTML */
        -moz-user-select: none; /* Old versions of Firefox */
        -ms-user-select: none; /* Internet Explorer/Edge */
        user-select: none; /* Non-prefixed version, currently
                                      supported by Chrome, Edge, Opera and Firefox */
      }
    

    在styles.css 和

      @HostBinding('class.no-select') get __() {
        return this.onDrag;
      }
    

    当元素滑动时不选择

    stackblitz 用于触摸/非触摸屏

    【讨论】:

    • 您好 Eliseo,我已经尝试过您的解决方案,但它不起作用?请看这个视频。 recordit.co/N5PkHr0w4D 然后还尝试了您提供的 stackblitz,遗憾的是它不起作用。
    • @Edward,该指令仅适用于触摸屏,我用另一个指令更新答案,改用 @HostListener 使用 rxjs 运算符 fromEvent
    • 我已经尝试了您提供的链接中的演示,它确实有效。但是当我在我们的解决方案中复制示例代码时,它有 CLI 错误。示例: o 重载匹配此调用。 Overload 1 of 5, '(observer?: NextObserver | ErrorObserver | CompletionObserver | undefined): Subscription',给出了以下错误。 '(event: MouseEvent) => void' 类型的参数不可分配给 'NextObserver 类型的参数 | ErrorObserver | CompletionObserver |下……
    • 您是否将指令包含在“app.module”中(如果您使用导出,则实际上是在您的组件所在的模块中或在 shared.module 中)?您使用什么 rxjs 版本(我认为适用于 rxjs 6 和 rxjs 7)?
    • 检查“括号”和“刹车”性能,您的代码有一个处于“错误位置”
    【解决方案3】:

    各位开发者大家好,感谢您的所有回答。顺便说一句,我使用https://www.npmjs.com/package/swipe-angular-list 来解决我的问题。

    【讨论】:

      猜你喜欢
      • 2018-12-09
      • 2017-05-20
      • 1970-01-01
      • 1970-01-01
      • 2016-12-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多