【问题标题】:Angular sortable list and ngFor last element referenceAngular 可排序列表和 ngFor 最后一个元素参考
【发布时间】:2018-08-14 13:36:57
【问题描述】:

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

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

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();
  }
}

【问题讨论】:

    标签: angular ngfor


    【解决方案1】:

    我相信 jQuery UI 只是将 HTML DOM 元素从一个 &lt;ul&gt; 移动到另一个,这会弄乱 Angular 的 *ngFor,因为它引用了 DOM 列表中呈现的最后一个元素。

    正如您的 UI 清楚显示的那样,移动的“待办事项”仍然具有以前的 listId,因此我认为您需要触发 Angular 中的更改,以便在重新渲染时重新处理 DOM 引用。

    我认为,如果您在将“待办事项”切换到另一个列表时挂钩该事件,并且实际上 将该待办事项从上一个列表中拼接出来并将其附加到另一个列表中,那么 Angular 将重新- 渲染 DOM,您将获得正常的预期行为。

    【讨论】:

      猜你喜欢
      • 2019-01-30
      • 1970-01-01
      • 1970-01-01
      • 2019-05-16
      • 2023-03-27
      • 1970-01-01
      • 2019-04-02
      • 2018-01-24
      • 2015-03-31
      相关资源
      最近更新 更多