CdkDragDrop and ngTemplateOutlet

此生再无相见时 提交于 2019-12-02 09:31:33

问题


I'm trying to use Drag&Drop features relased with Angular Material 7.

I separated my template in reusable pieces using ngTemplateOutlet and every option can be either a Thing™ or a nested Thing™ which have some more sub-Things™.

Nested Things™ are displayed as an expansion panel. I want all the first-level Things™ to be re-orderable as if thery were a list.

(Ok, ok, it's obvious that's a reorderable sidenav with normal and nested options, just pretend it's not so obvious)

This is the code I initially wrote.

<div cdkDropList (cdkDropListDropped)="dropItem($event)" lockAxis="y">
  <ng-container *ngFor="let thing of things">
      <ng-container
        *ngTemplateOutlet="!thing.children ? singleThing : multipleThing; context: { $implicit: thing }"
      ></ng-container>
    </ng-container>
</div>

<ng-template #singleThing let-thing>
  <div cdkDrag>
    <ng-container *ngTemplateOutlet="thingTemplate; context: { $implicit: thing }"></ng-container>
  </div>
</ng-template>

<ng-template #multipleOption let-thing>
  <mat-expansion-panel cdkDrag (cdkDropListDropped)="dropItem($event)">
    <mat-expansion-panel-header>
      <mat-panel-title>
        <p>Nested thing title</p>
        <span cdkDragHandle></span>
      </mat-panel-title>
    </mat-expansion-panel-header>

    <ng-container *ngFor="let childThing of thing.children">
      <div class="childThing">
        <ng-container *ngTemplateOutlet="thingTemplate; context: { $implicit: childThing }"></ng-container>
      </div>
    </ng-container>
  </mat-expansion-panel>
</ng-template>

<ng-template #thingTemplate let-thing>
  <p>I'm a thing!</p>
  <span cdkDragHandle></span>
</ng-template>

Problem: single Things™ are draggable, but they are not enforced as a list like cdkDropList should do, I can just drag them around everywhere.

I had a similar problem some time ago when trying to use template outlets and putting ng-templates back into the 'HTML flow' worked to solve that, so I tried the same.

<div cdkDropList (cdkDropListDropped)="dropItem($event)" lockAxis="y">
  <ng-container *ngFor="let thing of things">
      <ng-container
        *ngIf="!thing.children; then singleThing; else multipleThing"
      ></ng-container>
        <ng-template #singleThing>
          <div cdkDrag>
            <ng-container *ngTemplateOutlet="thingTemplate; context: { $implicit: thing }"></ng-container>
          </div>
        </ng-template>

        <ng-template #multipleOption>
          <mat-expansion-panel cdkDrag (cdkDropListDropped)="dropItem($event)">
            <mat-expansion-panel-header>
              <mat-panel-title>
                <p>Nested thing title</p>
                <span cdkDragHandle></span>
              </mat-panel-title>
            </mat-expansion-panel-header>

            <ng-container *ngFor="let childThing of thing.children">
              <div class="childThing">
                <ng-container *ngTemplateOutlet="thingTemplate; context: { $implicit: childThing }"></ng-container>
              </div>
            </ng-container>
          </mat-expansion-panel>
        </ng-template>
    </ng-container>
</div>

<ng-template #thingTemplate let-thing>
  <p>I'm a thing!</p>
  <span cdkDragHandle></span>
</ng-template>

And, of course why not, it works! Yeah, fine, but why?

Not much changed, we used a ngIf instead of the first ngTemplateOutlet and removed context bindings for the Thing™ because now both templates have its local variable reference thanks to the shared scope.

So, why exactly does it work in the second way and not in the first one?

Bonus points: is it possible to make it work keeping the first code structure which, to me, seems obviously more readable and clean?


回答1:


I had the same problem, I even reported this as an issue on GitHub.

It turns out to be caused by the separateness of cdkDropList from cdkDrag. cdkDrag must be in a tag nested inside the one with cdkDropList, otherwise the dragged element won't detect the drop zone.

The solution in your case would be an additional <div cdkDrag> below cdkDropList, and only under that you would call the template with ngTemplateOutlet.



来源:https://stackoverflow.com/questions/53210886/cdkdragdrop-and-ngtemplateoutlet

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!