【问题标题】:Angular2 - ngFor directive does not map the removed itemAngular2 - ngFor 指令不映射删除的项目
【发布时间】:2016-05-23 14:52:12
【问题描述】:

我有一个项目容器和项目模式,应该在其中添加子项目并从其容器中删除。添加很好,而删除什么也不做。当删除任何子项时,似乎 angular2 *ngFor 指令不起作用。

    import { NgFor} from 'angular2/common';
    import { bootstrap } from 'angular2/platform/browser';
    import { Component, View, Directive, OnDestroy, Input, enableProdMode } from 'angular2/core';
    import { CORE_DIRECTIVES} from 'angular2/common';


    @Component({selector: 'itemcontainer',})
    @View({ template: `<ul (click)="$event.preventDefault()">
                       <li *ngFor="#it of items">Any Item</li>
                       </ul>
                       <div><ng-content></ng-content></div>`,

            directives: [NgFor],
    })
    export class ItemContainer {
        public items: Array<Item> = [];

        public addItem(item: Item) {
            this.items.push(item);
        }

        public removeItem(item: Item) {
            var index = this.items.indexOf(item);
            if (index === -1) {
                return;
            }

            console.log(`Index about to remove: ${index} this.items length: ${this.items.length}`);
            this.items.slice(index, 1);
            console.log(`this.items length: ${this.items.length}`);
        }
    }

    @Directive({ selector: 'item' })
    export class Item implements OnDestroy {

        @Input() public heading: string;

        constructor(public itemcontainer: ItemContainer) {
            this.itemcontainer.addItem(this);
        }

        ngOnDestroy() {
            this.itemcontainer.removeItem(this);
        }
    }

    @Component({
        selector: 'my-app'
    })
    @View({
        template: `<div (click)="$event.preventDefault()">
            <button type="button" (click)="addItem()">Add item</button>
            <button type="button" (click)="removeItem()">Remove item</button>

        <itemcontainer>
            <item *ngFor="#containerItem of containerItems" [heading]="containerItem.title">Content </item>
        </itemcontainer>
    </div>`,

        directives: [CORE_DIRECTIVES, Item, ItemContainer],

    })

    class Tester {

        private counter: number = 2;

        public containerItems: Array<any> = [
            { title: 'Item1' },
            { title: 'Item2' },
        ];

        addItem() {
            this.containerItems.push({ title: `Item ${this.counter}` });
        }

        removeItem() {

            if (this.containerItems.length > 0) {
                this.containerItems.splice(this.containerItems.length - 1, 1);
            }
        }
    }

    enableProdMode();
    bootstrap(Tester);

这是添加和删除两个新项目后 DOM 的样子:

    <itemcontainer>
        <ul>
            <li>Any Item</li>
            <li>Any Item</li>
            <li>Any Item</li>
            <li>Any Item</li>
        </ul>
        <div>
            <item>Content </item>
            <item>Content </item>
        </div>
    </itemcontainer>

问题是 li 部分没有被删除。有什么想法吗?

(我用 angular 2.0.0-beta.3 和 2 对其进行了测试。)

【问题讨论】:

    标签: angular


    【解决方案1】:

    我和你有同样的问题,即使我使用了 splice 或 detecChange 它仍然无法正常工作 @McLac 提供了这样改造数组的方法

    this.items = [
        ...this.items.slice(0, index),
        ...this.items.slice(index + 1, this.items.length)
    ];
    

    救了我的命。它工作完美。 我有这样的代码:

    let idx = this.prds.indexOf(prd);
    if(idx==-1){
      return
    };
    /*    it was like this before, consol.log show deleted array correctly just the UI has no change..
    for(let i =0; i< this.prds.length; i++){
      if(this.prds[i]==prd){
        this.prds.splice(i,1);
        break;
      }
    }
    */
    this.prds = [
      ...this.prds.slice(0,idx),
      ...this.prds.slice(idx+1,this.prds.length)
    ];
    

    【讨论】:

      【解决方案2】:

      您需要使用splice() 而不是slice()。此处的 Angular 变化检测没有问题。

      this.items.splice(index, 1);
      

      NgFor 将循环遍历您的数组项,它会检测何时添加或删除某些内容。

      Plunker

      另外,你可以删除这些东西:

      import { NgFor} from 'angular2/common';
      import { CORE_DIRECTIVES} from 'angular2/common';
      
           directives: [NgFor],
      

      此外,您的数据中有循环引用。将您的代码更改为以下内容

      <li *ngFor="#it of items">Any Item {{it | json}}</li>
      

      并注意控制台中的错误:

      例外:TypeError:在 [Any Item {{it | ItemContainer 中的 json}}

      【讨论】:

      • 对于 Angular 2.4.0,这是正确的答案。我正在从 AngularJS 迁移 ng-repeat 代码,并且对这里有关 ngFor 更改检测的各种无效信息感到困惑。您只需编辑现有项目,通过 push() 添加新项目,或通过 splice() 删除项目,然后它将显示在视图中。
      【解决方案3】:

      问题可能与未正确设置前向引用有关(即,您不应在声明类之前使用它)。要解决此问题,您可以使用共享服务:

      export class SharedService {
          public items: Array<Item>=[];
      
          public addItem(item:Item) {
            this.items.push(item);
          }
      
          public removeItem(item:Item) {
             var index = this.items.indexOf(item);
            if (index >=0) {
              this.items.splice(index,1);
            }
          }
      }
      

      Item 构造函数/析构函数中使用共享服务:

      @Directive({ selector: 'item' })
      export class Item implements OnDestroy {
      
          @Input() public heading: string;
      
          constructor(public sharedService:SharedService) {
              this.sharedService.addItem(this);
          }
      
          ngOnDestroy() {
      
              this.sharedService.removeItem(this);
      
          }
      }
      

      还有你的ItemContainer:

      @Component({selector: 'itemcontainer',})
      @View({ template: `<ul (click)="$event.preventDefault()">
                         <li *ngFor="#it of items">Any Item </li>
                         </ul>
      
                         <div><ng-content></ng-content></div> `,
      
              directives: [NgFor],
      })
      export class ItemContainer {
          public items: Array<Item> = [];
          constructor(public sharedService:SharedService) {
             this.items = this.sharedService.items;
          }
      
      
      }
      

      Demo Plnkr

      【讨论】:

      • 我认为这里没有任何前向引用问题。我认为问题只是使用了slice() 而不是splice()。有兴趣可以看我的回答。
      【解决方案4】:

      是的,确实是变化检测。这对我有用:

          this.items = [
              ...this.items.slice(0, index),
              ...this.items.slice(index + 1, this.items.length)
          ];
      

      【讨论】:

        【解决方案5】:

        实际上,Angular2 仅在实例更改时才检测更改。我的意思是数组的实例不会在元素内部发生变化。

        你可以使用这个(参见第二个slice 调用):

        public removeItem(item: Item) {
          var index = this.items.indexOf(item);
          if (index === -1) {
            return;
          }
        
          console.log(`Index about to remove: ${index} this.items length: ${this.items.length}`);
          this.items.slice(index, 1);
          console.log(`this.items length: ${this.items.length}`);
        
          this.items = this.items.slice();
        }
        

        这个答案也可以帮助你:

        【讨论】:

        • 由于使用了ngFor,Angular 更改检测会在添加或删除项目时注意到。这里的变更检测没有问题。使用splice() 而不是slice() 可以轻松解决该问题。详情见我的回答。
        • 这很奇怪!我几乎可以肯定在过去我遇到过同样的问题,我像这样修复过......根据你的评论,我做了一个新的尝试(见这个 plunkr:plnkr.co/edit/YZkC74yOnJgSLEdzjcOE?p=preview)但我无法重现它......感谢您指出这一点,马克!
        • 也许您正在考虑将无状态管道与数组一起使用的情况——例如,stackoverflow.com/a/34497504/215945 解决此问题的一种方法是执行您在此处所做的操作...更改数组参考。
        猜你喜欢
        • 1970-01-01
        • 2016-09-23
        • 1970-01-01
        • 1970-01-01
        • 2023-03-09
        • 2019-05-22
        • 1970-01-01
        • 2017-06-02
        相关资源
        最近更新 更多