Angular可排序列表和ngFor最后一个元素参考

时间:2018-08-14 13:36:57

标签: angular ngfor

我正在学习Angular 6,并决定使用jQuerUI sortable plugin处理Todo应用程序中的拖放功能。插件按预期工作。我可以在列表之间移动项目,但是我注意到一些奇怪的行为。当我将最后一个项目从列表 1 移至列表 2 ,然后单击列表 1 上的“ +添加任务”按钮时,填写标题并保存后,它将呈现在列表 2 中,而不是预期的列表 1 中。

*ngFor是否持有对最后一个渲染元素的引用,然后仅在其后添加下一个元素?有办法防止这种行为吗?

GOOD and BAD behavior

list.component.html

<div>
  <div class="lists-container">
    <div class="list mx-4" *ngFor="let list of todosLists">
      <i class="fas fa-times cross cross-red" (click)="deleteList(list)"></i>
      <div class="mb-3">
        <p class="list-title">{{list.title}}</p>
      </div>
      <ul appDragnDropDirective [parentList]="list" (updated)="dragging($event)" class="col-sm list-size drag-drop empty-list-height">
        <li (click)="edit(todo)" *ngFor="let todo of list.todos"
            class="card text-white bg-primary mb-3 card-size">
          <div class="card-header">{{todo?.title}}</div>
          <div class="card-body">
            <p class="card-text">{{todo?.description}}</p>
            <p class="card-text"> listId={{todo?.listId}}</p>
          </div>
        </li>
        <div *ngIf="newTaskFormHidden.checked" class="card text-white bg-primary mb-3 card-size card-form">
          <div class="card-header">
            <input class="w-100 form-control" type="text" placeholder="Task title"
                   (input)="''" #title>
          </div>
          <div class="card-body">
            <button (click)="addTodo(title.value, list.id); title.value = ''" type="button" class="btn btn-success"
                    [disabled]="!title.value">Save
            </button>
            <button (click)="newTaskFormHidden.checked = !newTaskFormHidden.checked"
                    type="button" class="btn btn-danger float-right">Cancel
            </button>
          </div>
        </div>
      </ul>
      <input hidden type="checkbox" #newTaskFormHidden>
      <div *ngIf="!newTaskFormHidden.checked">
        <button
          class="btn btn-add-gray btn-block"
          type="button"
          (click)="newTaskFormHidden.checked = !newTaskFormHidden.checked">+ Add task
        </button>
      </div>
    </div>
    <div class="col-sm list-size">
      <input hidden type="checkbox" #newListFormHidden>
      <div *ngIf="newListFormHidden.checked" class="col-sm list">
        <div class="mb-3">
          <input (keyup.enter)="addList(listTitle.value); listTitle.value = ''"
                 class="w-100 form-control mb-2" type="text" placeholder="List title" (input)="''" #listTitle>
          <button type="button" class="btn btn-success"
                  [disabled]="!listTitle.value" (click)="addList(listTitle.value); listTitle.value = ''">Save
          </button>
          <button type="button" class="btn btn-danger float-right"
                  (click)="newListFormHidden.checked = !newListFormHidden.checked">Cancel
          </button>
        </div>
      </div>
      <button *ngIf="!newListFormHidden.checked" type="button" class="btn btn-outline-primary btn-block mt-3"
              (click)="newListFormHidden.checked = !newListFormHidden.checked">+ Add list
      </button>
    </div>
  </div>
</div>

list.component.ts(只需添加功能)

addTodo(title: string, listId: number): void {
    this.loggerService.add(`Add todo: title: ${title}, listId: ${listId}`);
    this.todoService.addTodo({title, listId} as Todo).subscribe(
      todo => {
        const list = this.todosLists.find(l => l.id === todo.listId);
        list.todos.push(todo);
      }
    );
  }

dragndrop.directive.ts

import {Directive, ElementRef, EventEmitter, Input, OnInit, Output} from '@angular/core';

declare let $: any;

@Directive({
  selector: '[appDragnDropDirective]'
})
export class DragndropDirective implements OnInit {
  @Input() cssClassToConnect;
  @Input() parentList;
  @Output() updated = new EventEmitter<any>();

  constructor(private el: ElementRef) { }

  ngOnInit() {
    this.setupDragAndDrop();
  }

  private setupDragAndDrop() {
    $(this.el.nativeElement).sortable({
      connectWith: this.cssClassToConnect || '.drag-drop',
      placeholder: 'card-placeholder',
      dropOnEmpty: true,
      tolerance: 'pointer',
      items: 'li:not(.card-form)',
      start: function (event, ui) {
        ui.placeholder.height(ui.item.outerHeight());
      },
      stop: (event, ui) => {
        console.log(event);
        console.log(ui.item.position());
        this.updated.emit(this.parentList);
      }
    }).disableSelection();
  }
}

1 个答案:

答案 0 :(得分:0)

我相信jQuery UI只是将HTML DOM元素从一个<ul>移到了另一个,这使Angular的*ngFor混乱了,因为它引用了从DOM列表中渲染的最后一个元素。

当您的UI清楚显示时,移动的“ todo”仍然具有前一个listId,因此我认为您需要触发Angular的更改,以便在重新渲染时重新处理DOM引用。

我认为,如果您在将“待办事项”切换到另一个列表并真正将待办事项从上一个列表中剪接并附加到另一个列表中时挂入了事件,那么Angular将会重新-渲染DOM,您将获得正常的预期行为。