ngClass ведет себя иначе, чем обычные классы с помощью cdk-drag-drop - PullRequest
1 голос
/ 26 февраля 2020

РЕДАКТИРОВАТЬ 2: Проблема была исправлена ​​удивительной командой CDK!


РЕДАКТИРОВАТЬ: Поскольку это кажется довольно странным поведением, я добавили проблему в официальном репозитории CDK . Тем не менее, не стесняйтесь предлагать любые идеи или методы работы.


В моем *cdkDropList есть различные типы элементов, которые нуждаются в различных возвышениях плашдолдера при их перетаскивании. Поскольку в массиве есть объекты со свойством type, я добавил эти типы как классы CSS и попытался добавить их динамически, используя [ngClass]. Однако эти динамически генерируемые классы ведут себя иначе, чем когда я устанавливаю их как «обычные» CSS классы.

Вот что происходит, когда я устанавливаю классы динамически:

enter image description here

Заполнитель и элементы в dropList перекрываются. Вот соответствующий код:

example.component.ts

contentItems: ContentItem[] = [
  { type: 'text', /* more props */ },
  { type: 'text', /* more props */ },
  { type: 'image', /* more props */ }
];

example.component.html

<div *ngFor="let item of contentItems" class="editor-item" cdkDrag>
  <div [ngClass]="['dropzone-placeholder', item.type]" *cdkDragPlaceholder>
    <p>{{ 'EDITOR.INSERT HERE' | translate }}</p>
  </div>
  <app-language-tab-editor *ngIf="item.type === 'text'"></app-language-tab-editor>
  <app-image-upload *ngIf="item.type === 'image'"></app-image-upload>
</div>

example.component.scss

$dropzone-placeholder-dark: #00973B;
$dropzone-placeholder-light: #00973B0D;
$text-placeholder-height: 135px;
$image-placeholder-height: 375px;

.dropzone-placeholder {
  border: 1px dashed $dropzone-placeholder-dark;
  color: $dropzone-placeholder-dark;
  background: $dropzone-placeholder-light;

  &.text {
    height: $text-placeholder-height;
  }

  &.image {
    height: $image-placeholder-height;
  }
}

Я только сейчас Есть два разных типа, но цель состоит в том, чтобы сделать его легко расширяемым, чтобы добавить больше позже. Я также уже пытался вместо этого использовать class="dropzone-placeholder {{ item.type }}" и [class]="'dropzone-placeholder ' + item.type", но безрезультатно.

После дальнейшего тестирования я также обнаружил, что он обычно не работает с использованием [ngClass], даже если мы не используем переменную. Использование [ngClass]="['dropzone-placeholder', 'text']" также не сработало.

Это ожидаемое поведение:

enter image description here

Заполнитель и элементы в dropList не перекрываются и вместо этого размещаются должным образом друг под другом. Такое поведение в настоящее время может быть достигнуто только путем регулярной установки классов, но на HTML довольно неприятно смотреть, поскольку в будущем код станет беспорядочно избыточным.

example.component.html

<div *ngFor="let item of contentItems" class="editor-item" cdkDrag>
  <div *ngIf="item.type === 'text'">
    <div class="dropzone-placeholder reorder text" *cdkDragPlaceholder>
      <p>{{ 'EDITOR.INSERT HERE' | translate }}</p>
    </div>
  </div>
  <div *ngIf="item.type === 'image'">
    <div class="dropzone-placeholder reorder image" *cdkDragPlaceholder>
      <p>{{ 'EDITOR.INSERT HERE' | translate }}</p>
    </div>
  </div>
  <app-language-tab-editor *ngIf="item.type === 'text'"></app-language-tab-editor>
  <app-image-upload *ngIf="item.type === 'image'"></app-image-upload>
</div>

Почему CDK ведет себя по-разному, когда классы не установлены традиционным способом? И как мне избежать написания избыточного обходного пути, упомянутого выше?

1 Ответ

0 голосов
/ 27 февраля 2020

Как указано в в этом ответе на вышеупомянутую проблему GitHub от @ Achilles1515, в настоящее время существует обходной путь, использующий исправленный код в файле TS. Обязательно следите за любыми обновлениями в CDK, которые исправляют это изначально.

Вот соответствующий код:

import { CdkDragDrop, DragRef, moveItemInArray } from '@angular/cdk/drag-drop';
import { Component, EmbeddedViewRef} from '@angular/core';

DragRef.prototype._createPlaceholderElement = function(): HTMLElement {
  const placeholderConfig = this._placeholderTemplate;
  const placeholderTemplate = placeholderConfig
    ? placeholderConfig.template
    : null;
  let placeholder: HTMLElement;

  if (placeholderTemplate) {
    this._placeholderRef = placeholderConfig!.viewContainer.createEmbeddedView(
      placeholderTemplate,
      placeholderConfig!.context
    );
    this._placeholderRef.detectChanges(); // This is the missing line in the CDK
    placeholder = getRootNode(this._placeholderRef, this._document);
  } else {
    placeholder = deepCloneNode(this._rootElement);
  }

  placeholder.classList.add('cdk-drag-placeholder');
  return placeholder;
};

/** Creates a deep clone of an element. */
function deepCloneNode(node: HTMLElement): HTMLElement {
  const clone = node.cloneNode(true) as HTMLElement;
  const descendantsWithId = clone.querySelectorAll('[id]');
  const descendantCanvases = node.querySelectorAll('canvas');

  // Remove the `id` to avoid having multiple elements with the same id on the page.
  clone.removeAttribute('id');

  for (let i = 0; i < descendantsWithId.length; i++) {
    descendantsWithId[i].removeAttribute('id');
  }

  // `cloneNode` won't transfer the content of `canvas` elements so we have to do it ourselves.
  // We match up the cloned canvas to their sources using their index in the DOM.
  if (descendantCanvases.length) {
    const cloneCanvases = clone.querySelectorAll('canvas');

    for (let i = 0; i < descendantCanvases.length; i++) {
      const correspondingCloneContext = cloneCanvases[i].getContext('2d');

      if (correspondingCloneContext) {
        correspondingCloneContext.drawImage(descendantCanvases[i], 0, 0);
      }
    }
  }

  return clone;
}

function getRootNode(
  viewRef: EmbeddedViewRef<any>,
  _document: Document
): HTMLElement {
  const rootNode: Node = viewRef.rootNodes[0];

  if (rootNode.nodeType !== _document.ELEMENT_NODE) {
    const wrapper = _document.createElement('div');
    wrapper.appendChild(rootNode);
    return wrapper;
  }

  return rootNode as HTMLElement;
}

Добавление этого в мой код исправило странное поведение с [ngClass].

...