Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swipe to delete in Angular 10

Hello my fellow developer. I would like just to ask for help for me to be to achieve the swipe delete feature on our Angular project.

Please see the screenshot below.

enter image description here

This is the code that was given to me to work with that feature.

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

I've tried the following plugin:

  • mat-list-touch
  • swipe-angular-list

but it won't work when importing in the submodule and used in component features.Thanks in advance.

like image 366
Eduwow Avatar asked Sep 03 '25 16:09

Eduwow


2 Answers

You can use Angular material Drag and Drop https://material.angular.io/cdk/drag-drop/examples

From that, you can create a swipe delete option.

Demo

<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);
}
like image 185
Chiranjaya Denuwan Avatar answered Sep 05 '25 07:09

Chiranjaya Denuwan


The events touchstart, touchend and touchmove are supported by angular. So you can imagine a directive like:

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 };
  }
}

You can use in an .html like

<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()
  }

See the stackblitz without any warranty

Update If we want that work in touched and no touched screen we can take another approach. Instead of use @HostListener we can use fromEvent rxjs operator. (else we need make a hostListenr over mousedown,mouseup and mousemove)

To control at time the touch events and mouse events we use rxjs merge operator, that received the value from the two observables. To use exact the same code, we use "map" in the touch events to convert the respose (a TouchEvent) in a MouseEvent.

The last point is use the "tap" to know if is a touchscreen or not. In a touch screen if we swipe down/up, we need scroll the window dwon/up is oure directive only allow horizontal movements

@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 };
  }
}

we add the class

.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 */
  }

in styles.css and

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

To not select when the element is swipping

The stackblitz for touch/no touch screen

like image 21
Eliseo Avatar answered Sep 05 '25 09:09

Eliseo