Angular Grid の行ドラッグ
Ignite UI for Angular Grid では、RowDrag がルート igx-grid
コンポーネントで初期化されて、rowDraggable
入力で設定できます。行ドラッグを有効にすると、ユーザーは行ドラッグ ハンドルを使用して行のドラッグを開始できます。
Angular Grid 行ドラッグの例
import { NgModule } from "@angular/core";
import { FormsModule } from "@angular/forms";
import { BrowserModule } from "@angular/platform-browser";
import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
import { AppComponent } from "./app.component";
import {
IgxGridModule,
IgxDragDropModule,
IgxButtonModule
} from "igniteui-angular";
import { GridDragBaseSampleComponent } from "./grid/grid-row-drag-base/grid-row-drag-base.component";
import { IgxPreventDocumentScrollModule } from "./directives/prevent-scroll.directive";
@NgModule({
bootstrap: [AppComponent],
declarations: [
AppComponent,
GridDragBaseSampleComponent
],
imports: [
BrowserModule,
BrowserAnimationsModule,
FormsModule,
IgxPreventDocumentScrollModule,
IgxGridModule,
IgxDragDropModule,
IgxButtonModule
],
providers: [],
entryComponents: [],
schemas: []
})
export class AppModule {}
ts
import { Component, ViewChild } from '@angular/core';
import { IDropDroppedEventArgs, IgxGridComponent } from 'igniteui-angular';
import { DATA } from '../../data/customers';
enum DragIcon {
DEFAULT = 'drag_indicator',
ALLOW = 'add'
}
@Component({
selector: 'app-grid-row-drag-base-sample',
styleUrls: ['./grid-row-drag-base.component.scss'],
templateUrl: 'grid-row-drag-base.component.html'
})
export class GridDragBaseSampleComponent {
@ViewChild('sourceGrid', { read: IgxGridComponent, static: true }) public sourceGrid: IgxGridComponent;
@ViewChild('targetGrid', { read: IgxGridComponent, static: true }) public targetGrid: IgxGridComponent;
public data1: any[];
public data2: any[];
constructor() {
this.data1 = DATA;
this.data2 = [];
}
public onRowDragEnd(args) {
args.animation = true;
}
public onDropAllowed(args: IDropDroppedEventArgs) {
this.targetGrid.addRow(args.dragData.data);
this.sourceGrid.deleteRow(args.dragData.key);
}
public onEnterAllowed(args) {
this.changeGhostIcon(args.drag.ghostElement, DragIcon.ALLOW);
}
public onLeaveAllowed(args) {
this.changeGhostIcon(args.drag.ghostElement, DragIcon.DEFAULT);
}
private changeGhostIcon(ghost, icon: string) {
if (ghost) {
const currentIcon = ghost.querySelector('.igx-grid__drag-indicator > igx-icon');
if (currentIcon) {
currentIcon.innerText = icon;
}
}
}
}
ts
<div class="grid-container">
<igx-grid [igxPreventDocumentScroll]="true" #sourceGrid [data]="data1" [height]="'480px'" [autoGenerate]="false" [rowDraggable]="true" [primaryKey]="'ID'" (rowDragEnd)="onRowDragEnd($event)">
<igx-column [field]="'ID'" [header]="'ID'" width="100px"></igx-column>
<igx-column [field]="'CompanyName'" [header]="'Company Name'" width="100px"></igx-column>
<igx-column [field]="'ContactName'" [header]="'Contact Name'" width="100px" [minWidth]="'60px'" [maxWidth]="'230px'"></igx-column>
<igx-column [field]="'ContactTitle'" [header]="'Contact Title'" width="100px"></igx-column>
<igx-column [field]="'Address'" [header]="'Address'" width="100px"></igx-column>
<igx-column [field]="'City'" [header]="'City'" width="100px"></igx-column>
<igx-column [field]="'Region'" [header]="'Region'" width="100px"></igx-column>
<igx-column [field]="'PostalCode'" [header]="'Postal Code'" width="100px"></igx-column>
<igx-column [field]="'Phone'" [header]="'Phone'" width="100px"></igx-column>
<igx-column [field]="'Fax'" [header]="'Fax'" width="100px"></igx-column>
</igx-grid>
<igx-grid [igxPreventDocumentScroll]="true" #targetGrid igxDrop [data]="data2" [height]="'480px'" [autoGenerate]="false" [emptyGridTemplate]="dragHereTemplate"
(enter)="onEnterAllowed($event)" (leave)="onLeaveAllowed($event)" (dropped)="onDropAllowed($event)" [primaryKey]="'ID'">
<igx-column [field]="'ID'" [header]="'ID'" width="100px"></igx-column>
<igx-column [field]="'CompanyName'" [header]="'Company Name'" width="100px"></igx-column>
<igx-column [field]="'ContactName'" [header]="'Contact Name'" width="100px" [minWidth]="'60px'" [maxWidth]="'230px'"></igx-column>
<igx-column [field]="'ContactTitle'" [header]="'Contact Title'" width="100px"></igx-column>
<igx-column [field]="'Address'" [header]="'Address'" width="100px"></igx-column>
<igx-column [field]="'City'" [header]="'City'" width="100px"></igx-column>
<igx-column [field]="'Region'" [header]="'Region'" width="100px"></igx-column>
<igx-column [field]="'PostalCode'" [header]="'Postal Code'" width="100px"></igx-column>
<igx-column [field]="'Phone'" [header]="'Phone'" width="100px"></igx-column>
<igx-column [field]="'Fax'" [header]="'Fax'" width="100px"></igx-column>
<ng-template #dragHereTemplate><div class="empty-grid">Drop a row to add it to the grid</div></ng-template>
</igx-grid>
</div>
html
.grid-container {
display: flex;
}
.grid-container .drop-area {
height: 500px;
width: 50%;
margin: 10px 20px;
}
igx-grid {
margin: 20px;
}
.empty-grid {
display: flex;
justify-content: center;
flex-direction: column;
text-align: center;
height: 100%;
}
scss
このサンプルが気に入りましたか? 完全な Ignite UI for Angularツールキットにアクセスして、すばやく独自のアプリの作成を開始します。無料でダウンロードできます。
構成
igx-grid
の行ドラッグを有効にするには、グリッドの rowDraggable
を true
に設定します。これが有効になると、行ドラッグ ハンドルが各行に表示されます。このハンドルは行ドラッグを開始するために使用できます。
<igx-grid [rowDraggable]="true">
...
</igx-grid>
html
ドラッグ ハンドルをクリックしてボタンを押しながらカーソルを動かすと、グリッドの rowDragStart
イベントが発生します。クリックをリリースすると、rowDragEnd
イベントが発生します。
以下は、行ドラッグをサポートするための igx-grid
の設定方法と、ドロップイベントの適切な処理方法についてのチュートリアルです。
この例では、あるグリッドから別のグリッドに行をドラッグし、それを最初のデータソースから削除し、それを 2 番目のデータソースに追加します。
ドロップ エリア
行ドラッグを簡単に有効にできました。次は行ドロップを処理する方法を設定する必要があります。
igxDrop
ディレクティブを使用して、行をドロップする場所を定義できます。
はじめに、アプリ モジュールに IgxDragDropModule
をインポートする必要があります。
import { ..., IgxDragDropModule } from 'igniteui-angular';
...
@NgModule({
imports: [..., IgxDragDropModule]
})
typescript
次にテンプレートでディレクティブのセレクターを使ってドロップ エリアを定義します。
この場合、ドロップ領域は行をドロップする 2 番目のグリッドになります。
<igx-grid #targetGrid igxDrop [data]="data2" [autoGenerate]="false" [emptyGridTemplate]="dragHereTemplate"
(enter)="onEnterAllowed($event)" (leave)="onLeaveAllowed($event)" (dropped)="onDropAllowed($event)" [primaryKey]="'ID'">
...
</igx-grid>
html
グリッドは最初空のため、ユーザーにとってより意味のあるテンプレートを定義します。
<ng-template #dragHereTemplate>
Drop a row to add it to the grid
</ng-template>
html
rowDragEnd
イベントの animation
パラメーターを使用して、ドロップできない領域に行がドロップされたときにアニメーションを有効にできます。true に設定されている場合、ドラッグされた行は、ドロップできない領域の上にドロップされると元の位置に戻ります。
以下はアニメーションを有効にする方法です。
export class IgxGridRowDragComponent {
public onRowDragEnd(args) {
args.animation = true;
}
}
typescript
ドロップ エリア イベント ハンドラー
テンプレートでドロップ領域を定義したら、コンポーネントの .ts
ファイルで igxDrop
の enter
、leave
、dropped
イベントを宣言する必要があります。
はじめに、enter
と leave
ハンドラーを見てみましょう。これらのメソッドでは、ドラッグの ghost のアイコンを変更して、行をドロップできる領域の上にあることをユーザーに示すことができます。
export class IgxGridRowDragComponent {
public onEnterAllowed(args) {
this.changeGhostIcon(args.drag.ghostElement, DragIcon.ALLOW);
}
public onLeaveAllowed(args) {
this.changeGhostIcon(args.drag.ghostElement, DragIcon.DEFAULT);
}
private changeGhostIcon(ghost, icon: string) {
if (ghost) {
const currentIcon = ghost.querySelector('.igx-grid__drag-indicator > igx-icon');
if (currentIcon) {
currentIcon.innerText = icon;
}
}
}
}
typescript
changeGhostIcon
private メソッドは、ドラッグ ゴースト内のアイコンを変更するだけです。メソッドのロジックは、アイコンを含む要素を検索し (ドラッグ インジケーター コンテナーに適用される igx-grid__drag-indicator
クラスを使用)、要素の内部テキストを渡されたものに変更します。
アイコンは material
フォントセットからのもので、別の enum
で定義されています。
enum DragIcon {
DEFAULT = 'drag_indicator',
ALLOW = 'add'
}
typescript
次に、ユーザーが実際にドロップ領域内に行をドロップしたときに何が起こるかを定義する必要があります。
export class IgxGridRowDragComponent {
@ViewChild('sourceGrid', { read: IgxGridComponent }) public sourceGrid: IgxGridComponent;
@ViewChild('targetGrid', { read: IgxGridComponent }) public targetGrid: IgxGridComponent;
public onDropAllowed(args) {
this.targetGrid.addRow(args.dragData.data);
this.sourceGrid.deleteRow(args.dragData.key);
}
}
typescript
次のように ViewChild
デコレータを使用して各グリッドに refenrece を定義し、ドロップを処理します。
- 削除される行のデータを含む行を
targetGrid
に追加します。
sourceGrid
からドラッグした行を削除します。
イベント引数 (args.dragData.data
) または他の行プロパティからの行データを使用する場合、行全体が参照として引数に渡されることに注意してください。つまり、ソースグリッドのデータと区別する必要がある場合は、必要なデータを複製する必要があります。
ドラッグ ゴーストのテンプレート化
ドラッグゴーストは、igx-grid
の本文内の <ng-template>
に適用される IgxRowDragGhost
ディレクティブを使用してテンプレート化できます。
<igx-grid>
...
<ng-template igxRowDragGhost>
<div>
<igx-icon fontSet="material">arrow_right_alt</igx-icon>
</div>
</ng-template>
...
</igx-grid>
html
以下は、行ドラッグと複数選択を有効にした igx-grid
で確認できる設定の結果です。以下のデモでは、現在ドラッグされている行の数を示します。
デモ
import { NgModule } from "@angular/core";
import { FormsModule } from "@angular/forms";
import { BrowserModule } from "@angular/platform-browser";
import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
import { AppComponent } from "./app.component";
import {
IgxGridModule,
IgxDragDropModule
} from "igniteui-angular";
import { GridMultipleRowDragComponent } from "./grid/grid-multiple-row-drag/grid-multiple-row-drag.component";
import { IgxPreventDocumentScrollModule } from "./directives/prevent-scroll.directive";
@NgModule({
bootstrap: [AppComponent],
declarations: [
AppComponent,
GridMultipleRowDragComponent
],
imports: [
BrowserModule,
BrowserAnimationsModule,
FormsModule,
IgxPreventDocumentScrollModule,
IgxGridModule,
IgxDragDropModule
],
providers: [],
entryComponents: [],
schemas: []
})
export class AppModule {}
ts
import { Component, ViewChild } from '@angular/core';
import { GridSelectionMode, IDropDroppedEventArgs, IgxGridComponent } from 'igniteui-angular';
import { DATA } from '../../data/customers';
@Component({
selector: 'app-grid-multiple-row-drag',
styleUrls: ['./grid-multiple-row-drag.component.scss'],
templateUrl: './grid-multiple-row-drag.component.html'
})
export class GridMultipleRowDragComponent {
@ViewChild('sourceGrid', { read: IgxGridComponent, static: true })
public sourceGrid: IgxGridComponent;
@ViewChild('targetGrid', { read: IgxGridComponent, static: true })
public targetGrid: IgxGridComponent;
public data1: any[];
public data2: any[];
public countIcon = 'drag_indicator';
public dragIcon = 'arrow_right_alt';
public selectionMode: GridSelectionMode = 'multiple';
constructor() {
this.data1 = DATA;
this.data2 = [];
}
public onRowDragEnd(args) {
args.animation = true;
}
public onDropAllowed(args: IDropDroppedEventArgs) {
let selected = false;
const ids = this.sourceGrid.selectedRows;
const selectedRowData = this.sourceGrid.data.filter((record) => ids.includes(record.ID));
selectedRowData.forEach((rowData) => {
selected = true;
this.targetGrid.addRow(rowData);
this.sourceGrid.deleteRow(rowData.ID);
});
if (selected === false) {
this.targetGrid.addRow(args.dragData.data);
this.sourceGrid.deleteRow(args.dragData.key);
}
}
public onEnter(args) {
this.dragIcon = 'add';
}
public onRowDragStart(args) {
const count = this.sourceGrid.selectedRows.length || 1;
this.countIcon = `filter_${count > 9 ? '9_plus' : `${count}`}`;
}
public onLeave(args) {
this.onRowDragStart(args);
this.dragIcon = 'arrow_right_alt';
}
}
ts
<div class="grid-container">
<igx-grid [igxPreventDocumentScroll]="true" #sourceGrid [data]="data1" [height]="'480px'" [autoGenerate]="false"
[rowDraggable]="true" [primaryKey]="'ID'" (rowDragStart)="onRowDragStart($event)"
(rowDragEnd)="onRowDragEnd($event)" [cellSelection]="'none'" [rowSelection]="selectionMode">
<igx-column [field]="'ID'" [header]="'ID'" width="100px"></igx-column>
<igx-column [field]="'CompanyName'" [header]="'Company Name'" width="100px"></igx-column>
<igx-column [field]="'ContactName'" [header]="'Contact Name'" width="100px" [minWidth]="'60px'"
[maxWidth]="'230px'"></igx-column>
<igx-column [field]="'ContactTitle'" [header]="'Contact Title'" width="100px"></igx-column>
<igx-column [field]="'Address'" [header]="'Address'" width="100px"></igx-column>
<igx-column [field]="'City'" [header]="'City'" width="100px"></igx-column>
<igx-column [field]="'Region'" [header]="'Region'" width="100px"></igx-column>
<igx-column [field]="'PostalCode'" [header]="'Postal Code'" width="100px"></igx-column>
<igx-column [field]="'Phone'" [header]="'Phone'" width="100px"></igx-column>
<igx-column [field]="'Fax'" [header]="'Fax'" width="100px"></igx-column>
<ng-template let-data igxRowDragGhost>
<div class="allow-drop">
<igx-icon family="material">{{dragIcon}}{{countIcon}}</igx-icon>
</div>
</ng-template>
</igx-grid>
<igx-grid [igxPreventDocumentScroll]="true" #targetGrid igxDrop [data]="data2" [height]="'480px'" [autoGenerate]="false"
[emptyGridTemplate]="dragHereTemplate" (enter)="onEnter($event)" (leave)="onLeave($event)"
(dropped)="onDropAllowed($event)" [primaryKey]="'ID'">
<igx-column [field]="'ID'" [header]="'ID'" width="100px"></igx-column>
<igx-column [field]="'CompanyName'" [header]="'Company Name'" width="100px"></igx-column>
<igx-column [field]="'ContactName'" [header]="'Contact Name'" width="100px" [minWidth]="'60px'"
[maxWidth]="'230px'"></igx-column>
<igx-column [field]="'ContactTitle'" [header]="'Contact Title'" width="100px"></igx-column>
<igx-column [field]="'Address'" [header]="'Address'" width="100px"></igx-column>
<igx-column [field]="'City'" [header]="'City'" width="100px"></igx-column>
<igx-column [field]="'Region'" [header]="'Region'" width="100px"></igx-column>
<igx-column [field]="'PostalCode'" [header]="'Postal Code'" width="100px"></igx-column>
<igx-column [field]="'Phone'" [header]="'Phone'" width="100px"></igx-column>
<igx-column [field]="'Fax'" [header]="'Fax'" width="100px"></igx-column>
<ng-template #dragHereTemplate>
<div class="empty-grid">Drop a row to add it to the grid</div>
</ng-template>
</igx-grid>
</div>
html
.grid-container {
display: flex;
}
.grid-container .drop-area {
height: 500px;
width: 50%;
margin: 10px 20px;
}
igx-grid {
margin: 20px;
}
.empty-grid {
display: flex;
justify-content: center;
flex-direction: column;
text-align: center;
height: 100%;
}
.allow-drop {
z-index: 1;
}
igx-icon{
margin-top: 10px;
}
scss
ドラッグ アイコンのテンプレート化
ドラッグ ハンドル アイコンは、グリッドの dragIndicatorIconTemplate
を使用してテンプレート化できます。作成している例で、アイコンをデフォルトのもの (drag_indicator
) から drag_handle
に変更します。
igxDragIndicatorIcon
を使用して igx-grid
の本文内にテンプレートを渡して変更できます。
<igx-grid>
...
<ng-template igxDragIndicatorIcon>
<igx-icon>drag_handle</igx-icon>
</ng-template>
...
</igx-grid>
html
新しいアイコン テンプレートの設定後、DragIcon enum
の DEFAULT
アイコンも調整する必要があるため、changeIcon
メソッドによって適切に変更されます。
enum DragIcon {
DEFAULT = "drag_handle",
...
}
typescript
ドロップ ハンドラが正しく設定されたら、準備完了です。
以下は、設定の結果です。
デモ
import { NgModule } from "@angular/core";
import { FormsModule } from "@angular/forms";
import { BrowserModule } from "@angular/platform-browser";
import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
import { AppComponent } from "./app.component";
import {
IgxGridModule,
IgxDragDropModule,
IgxIconModule,
IgxButtonModule
} from "igniteui-angular";
import { GridDragToGridSampleComponent } from "./grid/grid-row-drag-to-grid/grid-row-drag-to-grid.component";
import { IgxPreventDocumentScrollModule } from "./directives/prevent-scroll.directive";
@NgModule({
bootstrap: [AppComponent],
declarations: [
AppComponent,
GridDragToGridSampleComponent
],
imports: [
BrowserModule,
BrowserAnimationsModule,
FormsModule,
IgxPreventDocumentScrollModule,
IgxGridModule,
IgxDragDropModule,
IgxIconModule,
IgxButtonModule
],
providers: [],
entryComponents: [],
schemas: []
})
export class AppModule {}
ts
import { Component, ViewChild } from '@angular/core';
import { IDropDroppedEventArgs, IgxGridComponent } from 'igniteui-angular';
import { DATA } from '../../data/customers';
enum DragIcon {
DEFAULT = 'drag_handle',
ALLOW = 'add'
}
@Component({
selector: 'app-grid-row-drag-to-grid-sample',
styleUrls: ['./grid-row-drag-to-grid.component.scss'],
templateUrl: 'grid-row-drag-to-grid.component.html'
})
export class GridDragToGridSampleComponent {
@ViewChild('sourceGrid', { read: IgxGridComponent, static: true }) public sourceGrid: IgxGridComponent;
@ViewChild('targetGrid', { read: IgxGridComponent, static: true }) public targetGrid: IgxGridComponent;
public data1: any[];
public data2: any[];
constructor() {
this.data1 = DATA;
this.data2 = [];
}
public onRowDragEnd(args) {
args.animation = true;
}
public onDropAllowed(args: IDropDroppedEventArgs) {
this.targetGrid.addRow(args.dragData.data);
this.sourceGrid.deleteRow(args.dragData.key);
}
public onEnterAllowed(args) {
this.changeGhostIcon(args.drag.ghostElement, DragIcon.ALLOW);
}
public onLeaveAllowed(args) {
this.changeGhostIcon(args.drag.ghostElement, DragIcon.DEFAULT);
}
private changeGhostIcon(ghost, icon: string) {
if (ghost) {
const currentIcon = ghost.querySelector('.igx-grid__drag-indicator > igx-icon');
if (currentIcon) {
currentIcon.innerText = icon;
}
}
}
}
ts
<div class="grid-container">
<igx-grid [igxPreventDocumentScroll]="true" #sourceGrid [data]="data1" [height]="'480px'" [autoGenerate]="false" [rowDraggable]="true" [primaryKey]="'ID'" (rowDragEnd)="onRowDragEnd($event)">
<igx-column [field]="'ID'" [header]="'ID'" width="100px"></igx-column>
<igx-column [field]="'CompanyName'" [header]="'Company Name'" width="100px"></igx-column>
<igx-column [field]="'ContactName'" [header]="'Contact Name'" width="100px" [minWidth]="'60px'" [maxWidth]="'230px'"></igx-column>
<igx-column [field]="'ContactTitle'" [header]="'Contact Title'" width="100px"></igx-column>
<igx-column [field]="'Address'" [header]="'Address'" width="100px"></igx-column>
<igx-column [field]="'City'" [header]="'City'" width="100px"></igx-column>
<igx-column [field]="'Region'" [header]="'Region'" width="100px"></igx-column>
<igx-column [field]="'PostalCode'" [header]="'Postal Code'" width="100px"></igx-column>
<igx-column [field]="'Phone'" [header]="'Phone'" width="100px"></igx-column>
<igx-column [field]="'Fax'" [header]="'Fax'" width="100px"></igx-column>
<ng-template igxDragIndicatorIcon>
<igx-icon>drag_handle</igx-icon>
</ng-template>
</igx-grid>
<igx-grid [igxPreventDocumentScroll]="true" #targetGrid igxDrop [data]="data2" [height]="'480px'" [autoGenerate]="false" [emptyGridTemplate]="dragHereTemplate"
(enter)="onEnterAllowed($event)" (leave)="onLeaveAllowed($event)" (dropped)="onDropAllowed($event)" [primaryKey]="'ID'">
<igx-column [field]="'ID'" [header]="'ID'" width="100px"></igx-column>
<igx-column [field]="'CompanyName'" [header]="'Company Name'" width="100px"></igx-column>
<igx-column [field]="'ContactName'" [header]="'Contact Name'" width="100px" [minWidth]="'60px'" [maxWidth]="'230px'"></igx-column>
<igx-column [field]="'ContactTitle'" [header]="'Contact Title'" width="100px"></igx-column>
<igx-column [field]="'Address'" [header]="'Address'" width="100px"></igx-column>
<igx-column [field]="'City'" [header]="'City'" width="100px"></igx-column>
<igx-column [field]="'Region'" [header]="'Region'" width="100px"></igx-column>
<igx-column [field]="'PostalCode'" [header]="'Postal Code'" width="100px"></igx-column>
<igx-column [field]="'Phone'" [header]="'Phone'" width="100px"></igx-column>
<igx-column [field]="'Fax'" [header]="'Fax'" width="100px"></igx-column>
<ng-template #dragHereTemplate><div class="empty-grid">Drop a row to add it to the grid</div></ng-template>
</igx-grid>
</div>
html
.grid-container {
display: flex;
}
.grid-container .drop-area {
height: 500px;
width: 50%;
margin: 10px 20px;
}
igx-grid {
margin: 20px;
}
.empty-grid {
display: flex;
justify-content: center;
flex-direction: column;
text-align: center;
height: 100%;
}
scss
アプリケーション デモ
行ドラッグ イベントの使用
以下のデモは、行ドラッグ イベント情報を使用して、行がドロップされたカスタム コンポーネントとソース グリッド自体の両方の状態を変更する方法を示しています。
グリッドから月をドラッグして、それらを対応する惑星にドロップしてみてください。行ドラッグ ゴーストの背景は、ホバーされた惑星に応じて動的に変更されます。成功するとグリッド内の行が選択され、ドラッグは無効になります。惑星をクリックすると役に立つ情報が得られます。
import { NgModule } from "@angular/core";
import { FormsModule } from "@angular/forms";
import { BrowserModule } from "@angular/platform-browser";
import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
import { AppComponent } from "./app.component";
import {
IgxGridModule,
IgxDragDropModule,
IgxDialogModule
} from "igniteui-angular";
import { GridDragSampleComponent } from "./grid/grid-row-drag/grid-row-drag.component";
import { PlanetComponent } from "./grid/grid-row-drag/planet/planet.component";
import { IgxPreventDocumentScrollModule } from "./directives/prevent-scroll.directive";
@NgModule({
bootstrap: [AppComponent],
declarations: [
AppComponent,
GridDragSampleComponent,
PlanetComponent
],
imports: [
BrowserModule,
BrowserAnimationsModule,
FormsModule,
IgxPreventDocumentScrollModule,
IgxGridModule,
IgxDragDropModule,
IgxDialogModule
],
providers: [],
entryComponents: [],
schemas: []
})
export class AppModule {}
ts
import { Component, ViewChildren } from '@angular/core';
import { RowType } from 'igniteui-angular';
import { moonData, planetData } from './data';
import { PlanetComponent as PlanetComponent } from './planet/planet.component';
enum HoverClassList {
ALLOW = 'allow-drop',
DENY = 'deny-drop'
}
@Component({
selector: 'app-grid-row-drag-sample',
styleUrls: ['./grid-row-drag.component.scss'],
templateUrl: 'grid-row-drag.component.html'
})
export class GridDragSampleComponent {
@ViewChildren(PlanetComponent) public planets: PlanetComponent[];
public moonData: any[];
public planetData: any[];
constructor() {
this.moonData = moonData;
this.planetData = planetData;
}
public onRowDragStart(args) {
if (args.dragData.isSelected) {
args.cancel = true;
}
}
public onRowDragEnd(args) {
args.animation = true;
}
public onEnter(args, planet: PlanetComponent) {
args.drag.ghostElement.classList.add(
this.isDropAllowed(args.dragData.data.name, planet.name) ? HoverClassList.ALLOW : HoverClassList.DENY);
}
public onLeave(args) {
const dragGhost: HTMLElement = args.drag.ghostElement;
if (dragGhost) {
dragGhost.classList.remove(HoverClassList.ALLOW);
dragGhost.classList.remove(HoverClassList.DENY);
}
}
public onDrop(args, planet: PlanetComponent) {
const row: RowType = args.dragData;
if (this.isDropAllowed(row.data.name, planet.name)) {
row.data.planet = planet.name;
row.grid.selectRows([row.key]);
planet.moonsCount++;
}
}
private isDropAllowed(dragMoonName: string, dropPlanetName: string): boolean {
return this.planetData.filter((p) => p.name === dropPlanetName)[0].moons.includes(dragMoonName);
}
}
ts
<div class="container">
<div class="moon-info">
<igx-grid [igxPreventDocumentScroll]="true" [data]="moonData" [primaryKey]="'name'" [rowDraggable]="true" (rowDragStart)="onRowDragStart($event)" (rowDragEnd)="onRowDragEnd($event)">
<igx-column [field]="'name'" [header]="'Moon'" width="120px" [pinned]="true" [sortable]="true"></igx-column>
<igx-column [field]="'planet'" [header]="'Planet'" width="150px" [sortable]="true"></igx-column>
<igx-column [field]="'mass'" [header]="'Mass'" width="200px" [sortable]="true"></igx-column>
<igx-column [field]="'diameter'" [header]="'Diameter'" width="200px" [sortable]="true"></igx-column>
<igx-column [field]="'orbitalPeriod'" [header]="'Orbital Period'" width="200px" [sortable]="true"></igx-column>
</igx-grid>
</div>
<div class="solar-system">
<div class="planets">
<app-planet #planet *ngFor="let data of planetData" [data]="data"
igxDrop (enter)="onEnter($event, planet)" (leave)="onLeave($event)" (dropped)="onDrop($event, planet)"></app-planet>
</div>
<div class="sun-wrapper">
<div class="sun">
</div>
</div>
</div>
html
.container {
display: flex;
width: 100%;
}
.moon-info {
width: 60%;
padding: 25px;
}
.solar-system
{
width: 40%;
padding: 25px;
}
.solar-system {
display: flex;
flex-direction: column;
flex-flow: column;
}
.planets {
display: flex;
flex-direction: column;
flex-flow: column-reverse;
}
::ng-deep {
.allow-drop {
background-color: #72da67 !important;
opacity: 0.4;
}
.deny-drop {
background-color: #e41c77 !important;
opacity: 0.4;
}
}
planet.dragOver {
box-shadow: 5px 10px 18px #777;;
transition: all 300ms ease-in-out;
}
.sun-wrapper {
display: flex;
justify-content: center;
position: relative;
height: 30px;
width: 100%;
overflow: hidden;
}
.sun {
position: absolute;
border-radius: 50%;
border: 1px solid #777;;
width: 40%;
height: 100px;
background: #ffad00;
}
@media only screen and (max-width: 800px) {
.moon-info {
padding: 5px;
}
.solar-system {
padding: 5px;
}
}
scss
上記のデモで使用した行ドラッグ ゴーストに適用されるクラスは ::ng-deep 修飾子を使用しています。行ドラッグは内部グリッド機能であり、CSS カプセル化のためにアプリケーションレベルでアクセスできないためです。
行の並べ替えデモ
グリッドの行ドラッグ イベントと igxDrop
ディレクティブを使用して、ドラッグよる行の並べ替えるが可能なグリッドを作成できます。
すべてのアクションはグリッド本体の内側で発生するため、ここで igxDrop
ディレクティブをアタッチする必要があります:
<igx-grid #grid [data]="data" [rowDraggable]="true" [primaryKey]="'ID'" igxDrop (dropped)="onDropAllowed($event)">
...
</igx-grid>
html
グリッドに primaryKey
が指定されていることを確認してください!ロジックが行を適切に並べ替えられるように、行には一意の識別子が必要です。
rowDraggable
が有効になり、ドロップ エリアが定義されたら、ドロップ イベントの単純なハンドラーを実装する必要があります。行をドラッグするときは、以下を確認してください:
- 行はグリッド内にドロップされましたか?
- そうであれば、ドラッグされた行が他のどの行にドロップされましたか?
- ターゲット行が見つかれば、
data
配列内のレコードの位置を入れ替えます。
以下では、コンポーネントの .ts
ファイルに実装されていることがわかります。
export class GridRowReorderComponent {
public onDropAllowed(args) {
const event = args.originalEvent;
const currRowIndex = this.getCurrentRowIndex(this.grid.rowList.toArray(),
{ x: event.clientX, y: event.clientY });
if (currRowIndex === -1) { return; }
this.grid.deleteRow(args.dragData.key);
this.data.splice(currRowIndex, 0, args.dragData.data);
}
private getCurrentRowIndex(rowList, cursorPosition) {
for (const row of rowList) {
const rowRect = row.nativeElement.getBoundingClientRect();
if (cursorPosition.y > rowRect.top + window.scrollY && cursorPosition.y < rowRect.bottom + window.scrollY &&
cursorPosition.x > rowRect.left + window.scrollX && cursorPosition.x < rowRect.right + window.scrollX) {
return this.data.indexOf(this.data.find((r) => r.rowID === row.rowID));
}
}
return -1;
}
}
typescript
これらの簡単な手順で、ドラッグ/ドロップで行を並べ替えることができるグリッドを構成しました! 次のデモで、上記コードの動作を確認できます。
ドラッグ アイコンを押下しながら、グリッド内で好きな場所に行を移動できます。
import { NgModule } from "@angular/core";
import { FormsModule } from "@angular/forms";
import { BrowserModule } from "@angular/platform-browser";
import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
import { AppComponent } from "./app.component";
import {
IgxGridModule,
IgxDragDropModule
} from "igniteui-angular";
import { GridRowReorderComponent } from "./grid/grid-row-reorder-sample/grid-row-reorder";
import { IgxPreventDocumentScrollModule } from "./directives/prevent-scroll.directive";
@NgModule({
bootstrap: [AppComponent],
declarations: [
AppComponent,
GridRowReorderComponent
],
imports: [
BrowserModule,
BrowserAnimationsModule,
FormsModule,
IgxPreventDocumentScrollModule,
IgxGridModule,
IgxDragDropModule
],
providers: [],
entryComponents: [],
schemas: []
})
export class AppModule {}
ts
import { Component, QueryList, ViewChild } from '@angular/core';
import { IgxGridComponent, RowType } from 'igniteui-angular';
import { IgxRowDirective } from 'igniteui-angular/lib/grids/row.directive';
import { DATA } from '../../data/customers';
@Component({
selector: 'app-grid-row-reorder-sample',
styleUrls: ['grid-row-reorder.scss'],
templateUrl: 'grid-row-reorder.html'
})
export class GridRowReorderComponent {
@ViewChild('grid', { read: IgxGridComponent, static : true })
public grid: IgxGridComponent;
public data: any[];
constructor() {
this.data = DATA;
}
public onDropAllowed(args) {
const event = args.originalEvent;
const currRowIndex = this.getCurrentRowIndex(this.grid.rowList.toArray(),
{ x: event.clientX, y: event.clientY });
if (currRowIndex === -1) { return; }
this.grid.deleteRow(args.dragData.key);
this.data.splice(currRowIndex, 0, args.dragData.data);
}
private getCurrentRowIndex(rowList: IgxRowDirective[], cursorPosition) {
for (const row of rowList) {
const rowRect = row.nativeElement.getBoundingClientRect();
if (cursorPosition.y > rowRect.top + window.scrollY && cursorPosition.y < rowRect.bottom + window.scrollY &&
cursorPosition.x > rowRect.left + window.scrollX && cursorPosition.x < rowRect.right + window.scrollX) {
return this.data.indexOf(this.data.find((r) => r.ID === row.key));
}
}
return -1;
}
}
ts
<div class="grid-container">
<igx-grid [igxPreventDocumentScroll]="true" #grid [data]="data" [height]="'800px'" [autoGenerate]="false" [rowDraggable]="true"
[primaryKey]="'ID'" igxDrop (dropped)="onDropAllowed($event)">
<igx-column [field]="'ID'" [header]="'ID'"></igx-column>
<igx-column [field]="'CompanyName'" [header]="'Company Name'"></igx-column>
<igx-column [field]="'ContactName'" [header]="'Contact Name'" [minWidth]="'60px'"
[maxWidth]="'230px'"></igx-column>
<igx-column [field]="'ContactTitle'" [header]="'Contact Title'"></igx-column>
<igx-column [field]="'Address'" [header]="'Address'"></igx-column>
<igx-column [field]="'City'" [header]="'City'"></igx-column>
<igx-column [field]="'Region'" [header]="'Region'"></igx-column>
<igx-column [field]="'PostalCode'" [header]="'Postal Code'"></igx-column>
<igx-column [field]="'Phone'" [header]="'Phone'"></igx-column>
<igx-column [field]="'Fax'" [header]="'Fax'"></igx-column>
</igx-grid>
</div>
html
.grid-container {
display: flex;
padding: 20px;
}
.grid-container .drop-area {
height: 500px;
width: 50%;
margin: 10px 20px;
}
.empty-grid {
display: flex;
justify-content: center;
flex-direction: column;
text-align: center;
height: 100%;
}
scss
行ドラッグ シナリオでの UX の改善
現在カーソルの下にある行インデックスを取得できることで、豊富なカスタム機能を構築し、アプリケーションの UX を向上させる機会が得られます。たとえば、グリッド上のドラッグされた行の位置に基づいて、ドラッグ ゴーストを変更したり、ドロップ インジケーターを表示したりできます。この方法で実現できるもう 1 つの便利な動作は、グリッドの境界に達したときに、行をドラッグしながらグリッドを上下にスクロールすることです。
以下に、行の位置を知ることで実現できるいくつかのカスタム実装のスニペットの例を示します。
カーソル位置に基づいてドラッグ ゴーストを変更する
以下のスニペットでは、ドラッグ ゴースト内のテキストを変更して、ホバーされた行の名前を表示する方法を示しています。
まず、ドラッグ ゴーストに使用するテンプレートを指定します。dropName
プロパティは動的に変化し、カーソルが置かれている行の名前を取得します。
<ng-template igxRowDragGhost>
<div class="customGhost">
<div>{{ dropName }}</div>
</div>
</ng-template>
html
次に、終了した行のインスタンスを返すメソッド (行の並べ替えデモで使用されているものと同様) を定義します。
class MyRowGhostComponent {
private getRowDataAtPoint(rowList: IgxGridRowComponent[], cursorPosition: Point): any {
for (const row of rowList) {
const rowRect = row.nativeElement.getBoundingClientRect();
if (cursorPosition.y > rowRect.top + window.scrollY && cursorPosition.y < rowRect.bottom + window.scrollY &&
cursorPosition.x > rowRect.left + window.scrollX && cursorPosition.x < rowRect.right + window.scrollX) {
return this.data.find((r) => r.rowID === row.rowID);
}
}
return null;
}
}
typescript
最後に、IgxDragDirective.dragMove
イベント (ドラッグされた行に対して発行) を処理するために使用されるメソッドを作成します。このメソッドは、igxRowDragGhost
テンプレートで使用されているプロパティの値を変更し、強制的に再描画します。ドラッグしている特定の行の dragMove
イベントのみをサブスクライブし、行がドロップされるたびに (メモリ リークを防ぐために) サブスクライブを解除します。
class MyRowGhostComponent {
public ngAfterViewInit(): void {
this.grid.rowDragStart.pipe(takeUntil(this.destroy$)).subscribe(this.onRowDragStart.bind(this));
}
private onRowDragStart(e: IRowDragStartEventArgs) {
if (e !== null) {
this._draggedRow = e.dragData.rowData;
}
const directive = e.dragDirective;
directive.dragMove
.pipe(takeUntil(this.grid.rowDragEnd))
.subscribe(this.onDragMove.bind(this));
}
private onDragMove(args: IDragMoveEventArgs) {
const cursorPosition = this.getCursorPosition(args.originalEvent);
const hoveredRowData = this.getRowDataAtPoint(
this.grid.rowList.toArray(),
cursorPosition
);
if (!hoveredRowData) {
args.cancel = true;
return;
}
const rowID = hoveredRowData.ID;
if (rowID !== null) {
let newName = this.dropName;
if (rowID !== -1) {
const targetRow = this.grid.rowList.find((e) => {
return e.rowData.ID === rowID;
});
newName = targetRow?.rowData.Name;
}
if (newName !== this.dropName) {
this.dropName = newName;
args.owner.cdr.detectChanges();
}
}
}
}
typescript
カーソル位置に基づいたドロップ インジケーターの表示
次のセクションのデモでは、ドラッグされた行がドロップされる場所のインジケーターを表示する方法を確認します。このインジケーターは好きなようにカスタマイズできます - ドラッグされた行がドロップされる位置に配置されたプレースホルダー行、ドラッグされた行が現在ホバーされている行の上または下にドロップされるかどうかを示す境界線スタイルなどです。
カーソルの位置を追跡するために、行のドラッグを開始するときに IgxDragDirective
の dragMove
イベントにバインドします。
グリッドに primaryKey
が指定されていることを確認してください! ロジックが行を適切に並べ替えられるように、行には一意の識別子が必要です。
public ngAfterViewInit() {
this.grid.rowDragStart
.pipe(takeUntil(this.destroy$))
.subscribe(this.handleRowStart.bind(this));
}
private handleRowStart(e: IRowDragStartEventArgs): void {
if (e !== null) {
this._draggedRow = e.dragData.data;
}
const directive = e.dragDirective;
directive.dragMove
.pipe(takeUntil(this.grid.rowDragEnd))
.subscribe(this.handleDragMove.bind(this));
}
private handleDragMove(event: IDragMoveEventArgs): void {
this.handleOver(event);
}
private handleOver(event: IDragMoveEventArgs) {
const ghostRect = event.owner.ghostElement.getBoundingClientRect();
const rowIndex = this.getRowIndexAtPoint(this.grid.rowList.toArray(), {
x: ghostRect.x,
y: ghostRect.y
});
if (rowIndex === -1) {
return;
}
const rowElement = this.grid.rowList.find(
e => e.rowData.ID === this.grid.data[rowIndex].ID
);
if (rowElement) {
this.changeHighlightedElement(rowElement.element.nativeElement);
}
}
private clearHighlightElement(): void {
if (this.highlightedRow !== undefined) {
this.renderer.removeClass(this.highlightedRow, 'underlined-class');
}
}
private setHightlightElement(newElement: HTMLElement) {
this.renderer.addClass(newElement, 'underlined-class');
this.highlightedRow = newElement;
}
private changeHighlightedElement(newElement: HTMLElement) {
if (newElement !== undefined) {
if (newElement !== this.highlightedRow) {
this.clearHighlightElement();
this.setHightlightElement(newElement);
} else {
return;
}
}
}
typescript
行ドラッグでグリッドをスクロールする
非常に便利なシナリオは、ドラッグされた行がその上部または下部の境界に達したときにグリッドをスクロールできることです。これにより、グリッド内の行数にスクロールバーが必要な場合に、現在のビューポートの外側で行を並べ替えることができます。
以下に、ビューポートの端に到達したかどうかを確認し、必要に応じてスクロールするために使用する 2 つの方法の例を示します。isGridScrolledToEdge
は、グリッドをスクロールする方向 (「下」の場合は 1、「上」の場合は -1) の 1 つのパラメーターを受け入れ、その方向の最後の行に到達した場合は true
を返します。scrollGrid
メソッドは、グリッドをある方向 (1 または -1) にスクロールしようとしますが、グリッドがすでにその端にある場合は何もしません。
class MyGridScrollComponent {
private isGridScrolledToEdge(dir: 1 | -1): boolean {
if (this.grid.data[0] === this.grid.rowList.first.data && dir === -1) {
return true;
}
if (
this.grid.data[this.grid.data.length - 1] === this.grid.rowList.last.data &&
dir === 1
) {
return true;
}
return false;
}
private scrollGrid(dir: 1 | -1): void {
if (!this.isGridScrolledToEdge(dir)) {
if (dir === 1) {
this.grid.verticalScrollContainer.scrollNext();
} else {
this.grid.verticalScrollContainer.scrollPrev();
}
}
}
}
typescript
前の例で行ったように、特定の行の dragMove
イベントをサブスクライブします。dragMove
はカーソルが実際に移動したときにのみ起動されるため、行が端の 1 つにあるときにグリッドを自動スクロールするための便利で簡単な方法が必要ですが、ユーザーはマウスを移動しません 。500Ms
ごとにグリッドを自動スクロールする interval
を設定するメソッドを追加します。
ポインタがグリッドの端に達したときに interval
を作成してサブスクライブし、マウスが移動したり行がドロップされたりするたびに (カーソル位置に関係なく)、その interval
からサブスクライブを解除します。
class MyGridScrollComponent {
public ngAfterViewInit() {
this.grid.rowDragStart
.pipe(takeUntil(this.destroy$))
.subscribe(this.onDragStart.bind(this));
this.grid.rowDragEnd
.pipe(takeUntil(this.destroy$))
.subscribe(() => this.unsubInterval());
}
private onDragMove(event: IDragMoveEventArgs): void {
this.unsubInterval();
const dir = this.isPointOnGridEdge(event.pageY);
if (!dir) {
return;
}
this.scrollGrid(dir);
if (!this.intervalSub) {
this.interval$ = interval(500);
this.intervalSub = this.interval$.subscribe(() => this.scrollGrid(dir));
}
}
private unsubInterval(): void {
if (this.intervalSub) {
this.intervalSub.unsubscribe();
this.intervalSub = null;
}
}
}
typescript
以下は、上記の両方のシナリオの例です。ドロップ インジケーターを表示し、境界線の端に達したときにビューポートをスクロールします。
import { NgModule } from "@angular/core";
import { FormsModule } from "@angular/forms";
import { BrowserModule } from "@angular/platform-browser";
import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
import { AppComponent } from "./app.component";
import {
IgxGridModule,
IgxDragDropModule
} from "igniteui-angular";
import { GridDropIndicatorComponent } from "./grid/grid-drop-indicator/grid-drop-indicator";
import { IgxPreventDocumentScrollModule } from "./directives/prevent-scroll.directive";
@NgModule({
bootstrap: [AppComponent],
declarations: [
AppComponent,
GridDropIndicatorComponent
],
imports: [
BrowserModule,
BrowserAnimationsModule,
FormsModule,
IgxPreventDocumentScrollModule,
IgxGridModule,
IgxDragDropModule
],
providers: [],
entryComponents: [],
schemas: []
})
export class AppModule {}
ts
import { Component, ViewChild, AfterViewInit, OnDestroy, Renderer2 } from '@angular/core';
import { IDragMoveEventArgs, IDropDroppedEventArgs, IgxGridComponent, IRowDragStartEventArgs, Point } from 'igniteui-angular';
import { DATA } from '../../data/customers';
import { Subject, interval, Observable, Subscription } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { IgxRowDirective } from 'igniteui-angular/lib/grids/row.directive';
@Component({
selector: 'app-grid-drop-indicator',
styleUrls: ['grid-drop-indicator.scss'],
templateUrl: 'grid-drop-indicator.html'
})
export class GridDropIndicatorComponent implements AfterViewInit, OnDestroy {
@ViewChild('grid', { read: IgxGridComponent, static: true })
public grid: IgxGridComponent;
public data: any[];
private destroy$ = new Subject();
private intervalSub: Subscription;
private interval$: Observable<number>;
private _draggedRow: any;
private highlightedRow: HTMLElement;
constructor(private renderer: Renderer2) {
this.data = DATA;
}
public onDropAllowed(args: IDropDroppedEventArgs): void {
const event = args.originalEvent;
const currRowIndex = this.getRowIndexAtPoint(this.grid.rowList.toArray(), {
x: event.clientX,
y: event.clientY
});
if (currRowIndex === -1) {
return;
}
this.grid.deleteRow(this._draggedRow[this.grid.primaryKey]);
this.data.splice(currRowIndex, 0, this._draggedRow);
this.clearHighlightElement();
}
public ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
}
public ngAfterViewInit() {
this.grid.rowDragStart
.pipe(takeUntil(this.destroy$))
.subscribe(this.handleRowStart.bind(this));
this.grid.rowDragEnd
.pipe(takeUntil(this.destroy$))
.subscribe(() => {
this.unsubInterval();
this.clearHighlightElement();
});
}
private getRowIndexAtPoint(
rowList: IgxRowDirective[],
cursorPosition: Point
): number {
for (const row of rowList) {
const rowRect = row.nativeElement.getBoundingClientRect();
if (
cursorPosition.y > rowRect.top + window.scrollY &&
cursorPosition.y < rowRect.bottom + window.scrollY &&
cursorPosition.x > rowRect.left + window.scrollX &&
cursorPosition.x < rowRect.right + window.scrollX
) {
return this.data.indexOf(this.data.find(r => r.ID === row.key));
}
}
return -1;
}
private unsubInterval(): void {
if (this.intervalSub) {
this.intervalSub.unsubscribe();
this.intervalSub = null;
}
}
private handleRowStart(e: IRowDragStartEventArgs): void {
if (e !== null) {
this._draggedRow = e.dragData.data;
}
const directive = e.dragDirective;
directive.dragMove
.pipe(takeUntil(this.grid.rowDragEnd))
.subscribe(this.handleDragMove.bind(this));
}
private handleDragMove(event: IDragMoveEventArgs): void {
this.handleOver(event);
this.unsubInterval();
const dir = this.isPointOnGridEdge(event.pageY);
if (!dir) {
return;
}
this.scrollGrid(dir);
if (!this.intervalSub) {
this.interval$ = interval(500);
this.intervalSub = this.interval$.subscribe(() => this.scrollGrid(dir));
}
}
private isPointOnGridEdge(pageY: number): 1 | -1 {
const rect: ClientRect = this.grid.nativeElement
.querySelector('.igx-grid__tbody')
.getBoundingClientRect();
if (pageY >= rect.bottom) {
return 1;
} else if (pageY <= rect.top) {
return -1;
}
}
private isGridScrolledToEdge(dir: 1 | -1): boolean {
if (this.grid.data[0] === this.grid.rowList.first.data && dir === -1) {
return true;
}
if (
this.grid.data[this.grid.data.length - 1] ===
this.grid.rowList.last.data &&
dir === 1
) {
return true;
}
return false;
}
private scrollGrid(dir: 1 | -1): void {
if (!this.isGridScrolledToEdge(dir)) {
if (dir === 1) {
this.grid.verticalScrollContainer.scrollNext();
} else {
this.grid.verticalScrollContainer.scrollPrev();
}
}
}
private handleOver(event: IDragMoveEventArgs) {
const ghostRect = event.owner.ghostElement.getBoundingClientRect();
const rowIndex = this.getRowIndexAtPoint(this.grid.rowList.toArray(), {
x: ghostRect.x,
y: ghostRect.y
});
if (rowIndex === -1) {
return;
}
const rowElement = this.grid.rowList.find(
e => e.data.ID === this.grid.data[rowIndex].ID
);
if (rowElement) {
this.changeHighlightedElement(rowElement.element.nativeElement);
}
}
private clearHighlightElement(): void {
if (this.highlightedRow !== undefined) {
this.renderer.removeClass(this.highlightedRow, 'underlined-class');
}
}
private setHightlightElement(newElement: HTMLElement) {
this.renderer.addClass(newElement, 'underlined-class');
this.highlightedRow = newElement;
}
private changeHighlightedElement(newElement: HTMLElement) {
if (newElement !== undefined) {
if (newElement !== this.highlightedRow) {
this.clearHighlightElement();
this.setHightlightElement(newElement);
} else {
return;
}
}
}
}
ts
<div class="grid-container">
<igx-grid [igxPreventDocumentScroll]="true" #grid [data]="data" height="800px" width="1000px" [autoGenerate]="false" [rowDraggable]="true"
primaryKey="ID" igxDrop (dropped)="onDropAllowed($event)">
<igx-column field="ID" header="ID" width="100px"></igx-column>
<igx-column field="CompanyName" header="Company Name" width="100px"></igx-column>
<igx-column field="ContactName" header="Contact Name" width="100px" minWidth="60px"
maxWidth="230px"></igx-column>
<igx-column field="ContactTitle" header="Contact Title" width="100px"></igx-column>
<igx-column field="Address" header="Address" width="100px"></igx-column>
<igx-column field="City" header="City" width="100px"></igx-column>
<igx-column field="Region" header="Region" width="100px"></igx-column>
<igx-column field="PostalCode" header="Postal Code" width="100px"></igx-column>
<igx-column field="Phone" header="Phone" width="100px"></igx-column>
<igx-column field="Fax" header="Fax" width="100px"></igx-column>
</igx-grid>
</div>
html
.grid-container {
display: flex;
padding: 20px;
}
.grid-container .drop-area {
height: 500px;
width: 50%;
margin: 10px 20px;
}
.empty-grid {
display: flex;
justify-content: center;
flex-direction: column;
text-align: center;
height: 100%;
}
:host ::ng-deep {
.underlined-class {
border-bottom: 52px solid #d3d3d3;
}
}
scss
制限
現在、rowDraggable
ディレクティブに既知の制限はありません。
API リファレンス
その他のリソース
コミュニティに参加して新しいアイデアをご提案ください。