Угловое событие касания при наведении мыши при обработке множественного выбора

Я работаю с Angular 4, и у меня есть компонент, содержащий список <box> компоненты. Мне нужно, чтобы пользователь мог выбрать несколько полей в родительском компоненте.

В первой версии я обрабатываю события мыши с помощью mousedown, mouseover and mouseup, Для каждого события мыши a box component emits its id to the parent component, Пользователь может выбрать несколько блоков, щелкнув блок и перетащив мышь на другие поля.

Plunker

box.component.ts

import { Component, OnInit } from '@angular/core';
import { EventEmitter, Input, Output } from '@angular/core';

@Component({
  selector: 'box',
  styles: [`

  `],
  template: `
    <div class="box-container"
      (mousedown)="onMouseDown()"
      (mouseover)="onMouseOver()"
      (mouseup)="onMouseUp()">

     {{ id }}
   </div>
  `
})
export class BoxComponent implements OnInit {

  @Input() id: number;

  @Output() mouseDown: EventEmitter<number>;
  @Output() mouseOver: EventEmitter<number>;
  @Output() mouseUp: EventEmitter<number>;

  constructor() {
    this.mouseDown = new EventEmitter<number>();
    this.mouseOver = new EventEmitter<number>();
    this.mouseUp = new EventEmitter<number>();
  }

  ngOnInit() {

  }

  onMouseDown(): void {
    this.mouseDown.emit((this.id));
  }

  onMouseOver(): void {
    this.mouseOver.emit((this.id));
  }

  onMouseUp(): void {
    this.mouseUp.emit((this.id));
  }
}

app.component.ts

@Component({
  selector: 'my-app',
  template: `
    <div style="display: flex;">
      <box *ngFor="let id of ids" [id]="id"
           (mouseDown)="onMouseDown($event)"
           (mouseOver)="onMouseOver($event)"
           (mouseUp)="onMouseUp($event)">
      </box>
    </div>

    <br>

    <div style="display: flex;">
      Selected Ids: <span *ngFor="let id of selectedIds" style="padding-left: 10px;">{{ id }}</span>
    </div>
  `,
})
export class App {

  ids: [number];
  enableTracking: boolean;
  selectedIds: Set<number>;

  constructor() {

    this.enableTracking = false;
    this.ids  = [1, 2, 3, 4, 5];
    this.selectedIds = new Set<number>();
  }

  onMouseDown(boxId: number): void {
    this.enableTracking = true;
    this.selectedIds.clear();
    this.selectedIds.add(boxId);
  }

  onMouseOver(boxId: number): void {
    if (this.enableTracking) {
      this.selectedIds.add(boxId);  
    }
  }

  onMouseUp(boxId: number): void {
    this.enableTracking = false;

    console.log('Selected Ids: ' + JSON.stringify(this.selectedIds));
  }
}

Во второй версии я пытаюсь добиться того же, используя сенсорные события: touchstart, touchmove and touchend,

Plunker

box.component.ts

import { Component, OnInit } from '@angular/core';
import { EventEmitter, Input, Output } from '@angular/core';

@Component({
  selector: 'box',
  styles: [`

  `],
  template: `
    <div class="box-container"
      (touchstart)="onTouchStart()"
      (touchmove)="onTouchMove()"
      (touchend)="onTouchEnd()">

     {{ id }}
   </div>
  `
})
export class BoxComponent implements OnInit {

  @Input() id: number;

  @Output() touchStart: EventEmitter<number>;
  @Output() touchMove: EventEmitter<number>;
  @Output() touchEnd: EventEmitter<number>;

  constructor() {
    this.touchStart = new EventEmitter<number>();
    this.touchMove = new EventEmitter<number>();
    this.touchEnd = new EventEmitter<number>();
  }

  ngOnInit() {

  }

  onTouchStart(): void {
    this.touchStart.emit((this.id));
  }

  onTouchMove(): void {
    this.touchMove.emit((this.id));
  }

  onTouchEnd(): void {
    this.touchEnd.emit((this.id));
  }
}

app.component.ts

@Component({
  selector: 'my-app',
  template: `
    <div style="display: flex;">
      <box *ngFor="let id of ids" [id]="id"
           (touchStart)="onTouchStart($event)"
           (touchMove)="onTouchMove($event)"
           (touchEnd)="onTouchEnd($event)">
      </box>
    </div>

    <br>

    <div style="display: flex;">
      Selected Ids: <span *ngFor="let id of selectedIds" style="padding-left: 10px;">{{ id }}</span>
    </div>
  `,
})
export class App {

  ids: [number];
  enableTracking: boolean;
  selectedIds: Set<number>;

  constructor() {

    this.enableTracking = false;
    this.ids  = [1, 2, 3, 4, 5];
    this.selectedIds = new Set<number>();
  }

  onTouchStart(boxId: number): void {
    this.enableTracking = true;
    this.selectedIds.clear();
    this.selectedIds.add(boxId);
  }

  onTouchMove(boxId: number): void {
    if (this.enableTracking) {
      this.selectedIds.add(boxId);  
    }
  }

  onTouchEnd(boxId: number): void {
    this.enableTracking = false;

    console.log('Selected Ids: ' + JSON.stringify(this.selectedIds));
  }
}

Тем не менее, это не работает, как ожидалось, потому что touchmove событие всегда испускают box-id associated to touchstart событие. В первой версии каждый раз, когда я перехожу к другому компоненту коробки, mouseover испускает идентификатор коробки current-active коробочный компонент.

Как мне справиться с этим множественным выбором в сенсорных устройствах?

0 ответов

Немного поздно, но этот способ может быть полезен:

  1. Добавьте данные индекса в элемент DOM в качестве атрибута, который можно использовать для поиска элемента angular
  2. Используйте elementFromPoint, чтобы найти фактический элемент DOM, который вы ищете
  3. Анализируйте данные индекса из элемента и используйте их для выбора соответствующего углового элемента.

Я использую эту функцию, которая содержит некоторую фильтрацию:

public touchMoveHandler(event) {
    // Allow dragging only if it was started on a specific sets of classes
    if (!event.touches[0].target.classList.contains('free') || event.touches[0].target.classList.contains('inactive-for-user')) {
        return;
    }

    // Prevent scroll by touch
    event.preventDefault();

    // Find the actual DOM element under the coordinates
    const targetElement = (document.elementFromPoint(event.changedTouches[0].clientX, event.changedTouches[0].clientY));

    // Only match elements that I want to listen
    if (!targetElement.classList.contains('free')) {
        return;
    }

    // Get element index attribute that points to my array
    const id = targetElement['getAttribute']('data-block-index');

    // Find it in my array
    const currentBlock = this.blocks[id];

    ...
}

В HTML:

        (touchmove)="touchMoveHandler($event)"
        [attr.data-block-index]="i"

Не самый элегантный способ, лучше не нашел.

Другие вопросы по тегам