如何用返回新值的setter替换`@computed`?

n9vozmp4  于 2022-09-28  发布在  其他
关注(0)|答案(2)|浏览(155)

问题

我经常使用这种计算属性,setter只返回新值:

@computed('args.myValue')
  get myValue() {
    return this.args.myValue;
  }
  set myValue(newValue) {
    return newValue; // <==== this is no longer valid with native setter
  }

这几乎没有什么作用:
1.将初始值设置为args.myValue
1.允许更改值(通常通过<Input @value={{this.myValue}} />
1.args.myValue更改时恢复默认值
问题是本地setter不能return任何值。
请注意,我可能会找到一个“hackish”解决方案,但我希望代码遵循新的EmberJS约定,以避免以后痛苦的更新。

我尝试过的事情

手动缓存

@tracked _myValue = null;

  get myValue() {
    return this._myValue || this.args.myValue;
  }
  set myValue(newValue) {
    this._myValue = newValue;
  }

这不起作用,因为_myValue总是在第一个myValue=(newValue)之后设置。为了使其工作,应该有某种观察员在args.myValue更改时将其重置为null
可悲的是,observers are no longer part of EmberJS with native classes

{{unbound}}助手

<Input @value={{unbound this.myValue}} />

正如预期的那样,它不起作用,因为它只是不更新myValue

{{unbound}}助手与event.target.value处理相结合

<Input @value={{unbound this.myValue}} {{on "keyup" this.keyPressed}} />
get myValue() {
    return this.args.myValue;
  }

  @action keyPressed(event) {
    this.doStuffThatWillUpdateAtSomeTimeMyValue(event.target.value);
  }

但当args.myValue更改时,Input仍未更新。

初始代码

下面是一个更具体的使用示例:

组件

// app/components/my-component.js

export default class MyComponent extends Component {

  @computed('args.projectName')
  get projectName() {
    return this.args.projectName;
  }
  set projectName(newValue) {
    return newValue; // <==== this is no longer valid with native setter
  }

  @action
  searchProjects() {
    /* event key stuff omitted */
    const query = this.projectName;
    this.args.queryProjects(query);
  }
}
{{! app/components/my-component.hbs }}

<Input @value={{this.projectName}} {{on "keyup" this.searchProjects}} />

控制器

// app/controllers/index.js

export default class IndexController extends Controller {

  get entry() {
    return this.model.entry;
  }

  get entryProjectName() {
    return this.entry.get('project.name');
  }

  @tracked queriedProjects = null;

  @action queryProjects(query) {
    this.store.query('project', { filter: { query: query } })
      .then((projects) => this.queriedProjects = projects);
  }

  @action setEntryProject(project) {
    this.entry.project = project;
  }
}
{{! app/templates/index.hbs }}

<MyComponent 
  @projectName={{this.entryProjectName}} 
  @searchProjects={{this.queryProjects}} />

当在控制器中设置queriedProjects时,组件会显示它们。
单击其中一个搜索结果时,控制器会更新setEntryProject

nfeuvbwi

nfeuvbwi1#

根据this Ember.js discussion
Net,我个人的观点是,正是由于这个原因,通常最好使用常规<input>而不是e1d1e组件,并连接您自己的事件侦听器。这将使您负责设置项目。动作中的数量值,但它也消除了使用两种不同的方法设置相同值的最后一个问题,并且它还为您提供了一个机会来处理其他事件。
我通过使用标准<input>找到了解决这个问题的方法,这似乎是解决问题的“正确方法”(我非常感谢任何能告诉我更好方法的评论):

{{! app/components/my-component.hbs }}

<input value={{this.projectName}} {{on "keyup" this.searchProjects}} />
// app/components/my-component.js

@action
searchProjects(event) {
  /* event key stuff omitted */
  const query = event.target.value;
  this.args.queryProjects(query);
}

如果我需要将输入值保留为属性,我可以这样做:

{{! app/components/my-component.hbs }}

<input value={{this.projectName}} 
  {{on "input" this.setProjectQuery}} 
  {{on "keyup" this.searchProjects}} />
// app/components/my-component.js

@action setProjectQuery(event) {
  this._projectQuery = event.target.value;
}

@action
searchProjects( {
  /* event key stuff omitted */
  const query = this._projectQuery;
  this.args.queryProjects(query);
}

编辑

请注意,以下解决方案有一个缺点:它没有提供一种简单的方法来在输入值不变时将其重置为this.projectName,例如在焦点输出后。
为了解决这个问题,我添加了一些代码:

{{! app/components/my-component.hbs }}

<input value={{or this.currentInputValue this.projectName}}
  {{on "focusin" this.setCurrentInputValue}}
  {{on "focusout" this.clearCurrentInputValue}}
  {{on "input" this.setProjectQuery}} 
  {{on "keyup" this.searchProjects}} />
// app/components/my-component.js
// previous code omitted

@tracked currentInputValue = null;

@action setCurrentInputValue() {
  this.currentInputValue = this.projectName;
}

@action clearCurrentInputValue() {
  this.currentInputValue = null;
}
fcy6dtqo

fcy6dtqo2#

对于这个2源绑定场景,有一种非常通用和简洁的方法,可以使用任何交互式输入元素及其他元素。
考虑到您的第一次尝试(»手动缓存«):

  • 我们有一个通过getter和setter的函数反馈循环;setter不需要返回值,因为它无条件地触发绑定的getter(this._myValue不需要跟踪)
  • 需要开关才能将更改的外部预设值(this.args.myValue)注入此循环
  • 这是通过基于预设值的GUID哈希Map实现的,该预设值为交互输入建立了一个 transient 范围;因此,更改预设值注入和交互输入会相互覆盖:
// app/components/my-component.js
import Component from '@glimmer/component';
import { guidFor } from '@ember/object/internals';

export default class extends Component {

    // external preset value by @stringArg
    _myValue = new Map();

    get myValue() {
        let currentArg = this.args.stringArg || null;
        let guid = guidFor(currentArg);
        if (this._myValue.has(guid)) {
            return this._myValue.get(guid)
        }
        else {
            this._myValue.clear(); // (optional) avoid subsequent GUID reuse of primitive types (Strings)
            return currentArg;
        }
    }

    set myValue(value) {
        this._myValue.set(guidFor(this.args.stringArg || null), value);
    }
}
// app/components/my-component.hbs
<Input @value={{mut this.myValue}} />

https://ember-twiddle.com/a72fa70c472dfc54d03d040f0d849d17

相关问题