【问题标题】:Aurelia repeat.for does not refresh when model changes模型更改时 Aurelia repeat.for 不刷新
【发布时间】:2019-08-14 17:58:27
【问题描述】:

我想用 Aurelia 构建一个简单的自定义组件,允许用户输入一个或多个字符串。当有多个项目时,列表应为列表中的每个项目显示删除按钮。

我的问题是当列表中有多个项目时,列表的第一项不显示 删除 按钮。 This is how it looks

这是自定义列表组件的代码和 html:

查看

<template>
  <div repeat.for="item of items">
    <input type="text" value.bind="items[$index]">
    <button click.delegate="remove($index)" 
            if.bind="hasMoreThanOne()">Remove</button>
  </div>
  <button click.delegate="add()">Add</button>
</template>

视图模型

export class List {
  items: string[];

  constructor() {
    this.items = [];
    this.add();
  }

  add() {
    this.items.push("");
  }

  hasMoreThanOne() {
    return this.items.length > 1;
  }

  remove(index) {
    this.items.splice(index,1);
  }
}

我的问题有两个:

  • 为什么当列表长度改变时第一个列表项没有自动更新?
  • 如何使第一个项目也显示删除按钮?

【问题讨论】:

    标签: aurelia


    【解决方案1】:

    Aurelia 将任何属于bind 命令的函数视为纯函数。这意味着在传递给函数的参数发生变化之前,它不会再次调用该函数。由于hasMoreThanOne() 的返回值会根据不是函数参数的东西而改变(自然,因为函数没有任何参数),Aurelia 不会再次调用该函数。

    Aurelia 在数组更改时不重新评估函数的原因是中继器已优化并且看到数组中的第一项没有更改,因此它只是继续使用它拥有的现有 DOM。使用正确创建的视图,这有助于大大提高性能,但在您的情况下,它会导致不必要的问题。

    您找到了一种处理此问题的非最佳方法,即使用 getter。这不是最佳的原因是,默认情况下,Aurelia 每 200 毫秒使用一次脏检查来检查对 getter 的更改。这可以解决您遇到的问题,但对于性能来说并不理想。

    考虑到hasMoreThanOne() 函数的简单程度,最简单的选择就是简单地将函数内联到绑定中,如下所示:

    <template>
      <div repeat.for="item of items">
        <input type="text" value.bind="items[$index]">
        <button click.delegate="remove($index)" 
                if.bind="items.length > 1">Remove</button>
      </div>
      <button click.delegate="add()">Add</button>
    </template>
    

    老实说,我可能会这样处理。

    您也可以像现在一样使用 getter,但将 computedFrom 装饰器附加到它以防止脏检查:

    import {computedFrom} from 'aurelia-framework';
    
    export class List {
      items: string[];
    
      constructor() {
        this.items = [];
        this.add();
      }
    
      add() {
        this.items.push("");
      }
    
      @computedFrom('items.length')
      get hasMoreThanOne() {
        return this.items.length > 1;
      }
    
      remove(index) {
        this.items.splice(index,1);
      }
    }
    

    这将为您提供与我上面使用的内联绑定完全相同的性能,但需要编写更多代码。

    【讨论】:

    • 感谢您花时间彻底解释。根据快速测试,您提供的解决方案似乎确实快了一些。
    【解决方案2】:

    如果您将items.length 传递给hasMoreThanOne(),那么每次长度更改时,Aurelia 都会重新计算该方法。像这样:hasMoreThanOne(items.length)

    在 Html 中,除非参数已更改,否则 Aurelia 不会重新计算函数。

    我刚刚解决了一个使用I18n 的函数的问题,functiontable 中。 当语言改变时,该函数不会重新评估该函数。通过将boolean 传递给我在event 上更改的函数,我解决了它。即使函数的实际数据没有改变改变的布尔值触发 Aurelia 重新评估整个函数。

    像这样:

    <td>
    ${functionToTrigger(item.value, triggerBool}
    </td>
    

    然后

    this.eventAggregator('notactualli18nEvent:changed',()=> {this.triggerBool = !triggerBool});
    

    【讨论】:

      【解决方案3】:

      我完全靠运气解决了这个问题。将hasMoreThanOne() 函数转换为属性解决了这个问题,现在列表中的第一项也可以看到 remove 按钮。

      以下是我所做的更改:

      hasMoreThanOne() {
          return this.items.length > 1;
      }
      

      现在

      get hasMoreThanOne() {
          return this.items.length > 1;
      }
      

      和类似的视图变化:

      <button click.delegate="remove($index)" 
                  if.bind="hasMoreThanOne()">Remove</button>
      

      现在

      <button click.delegate="remove($index)" 
                  if.bind="hasMoreThanOne">Remove</button>
      

      我仍然很困惑为什么这会改变任何东西。因此,如果有人能够向我解释这种行为,我将非常高兴听到。

      【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-06-26
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多