使用 cdk-drag-drop 的 ngClass 行为与常规 类 不同
ngClass behaving differently than regular classes with cdk-drag-drop
编辑 2: 这个问题已经被了不起的 CDK 团队解决了!
编辑: 因为这看起来很奇怪,所以我现在添加了 an issue in the official CDK repository。但是,请随时提出任何想法或解决方法。
我的 *cdkDropList
中有不同类型的元素,在拖动它们时需要不同的占位符高度。由于该数组具有 属性 type
的对象,我将这些类型添加为 CSS 类 并尝试使用 [ngClass]
动态添加它们。但是,这些动态生成的 类 的行为与我将它们设置为 "regular" 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 的行为不同?我怎样才能避免编写上面提到的冗余解决方法?
更新:此问题已在 CDK 包中本地修复,使解决方法过时。
正如@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]
的奇怪行为。
编辑 2: 这个问题已经被了不起的 CDK 团队解决了!
编辑: 因为这看起来很奇怪,所以我现在添加了 an issue in the official CDK repository。但是,请随时提出任何想法或解决方法。
我的 *cdkDropList
中有不同类型的元素,在拖动它们时需要不同的占位符高度。由于该数组具有 属性 type
的对象,我将这些类型添加为 CSS 类 并尝试使用 [ngClass]
动态添加它们。但是,这些动态生成的 类 的行为与我将它们设置为 "regular" 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 的行为不同?我怎样才能避免编写上面提到的冗余解决方法?
更新:此问题已在 CDK 包中本地修复,使解决方法过时。
正如@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]
的奇怪行为。