【问题标题】:ngClass behaving differently than regular classes with cdk-drag-dropngClass 的行为与使用 cdk-drag-drop 的常规类不同
【发布时间】:2020-02-26 09:44:22
【问题描述】:

编辑 2:这个问题已经由了不起的 CDK 团队解决了!


编辑:由于这似乎是一种很奇怪的行为,我现在添加了an issue in the official CDK repository。不过,您可以随意提出任何想法或解决方法。


我的*cdkDropList 中有不同类型的元素,在拖动它们时需要不同的占位符高度。由于该数组具有属性为type 的对象,因此我将这些类型添加为CSS 类并尝试使用[ngClass] 动态添加它们。但是,这些动态生成的类的行为与我将它们设置为“常规”CSS 类时的行为不同。

当我动态设置类时会发生这种情况:

占位符和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']" 也不起作用。

这是预期的行为:

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 的行为会有所不同?以及如何避免编写上面提到的冗余解决方法?

【问题讨论】:

    标签: angular drag-and-drop angular-cdk angular-cdk-drag-drop


    【解决方案1】:

    更新:该问题已在 CDK-Package 中以本机方式修复,因此该解决方法已过时。


    正如@Achilles1515 在this answer to the aforementioned GitHub issue 中指出的那样,目前有一种解决方法,即在 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] 的奇怪行为。

    【讨论】:

      猜你喜欢
      • 2020-03-22
      • 1970-01-01
      • 1970-01-01
      • 2020-03-14
      • 1970-01-01
      • 1970-01-01
      • 2020-06-02
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多