简体   繁体   English

如何跟踪 Glimmer 组件中的嵌套属性?

[英]How to track a nested attribute in a Glimmer component?

I am building my first Glimmmer component in Ember and I had something like this in my template:我正在 Ember 中构建我的第一个 Glimmmer 组件,我的模板中有这样的东西:

<p>Some val: {{this.my_obj.some.deeply.nested.path.to.val}}</p>

I then made a corresponding .js component file and thought I would try to remove the long path from the HTML.然后我制作了一个相应的.js组件文件,并认为我会尝试从 HTML 中删除长路径。

My first attempt was to make a getter:我的第一次尝试是制作一个吸气剂:

get val() {
  return this.my_obj.some.deeply.nested.path.to.val;
}

With the template now:现在使用模板:

<p>Some val: {{this.val}}</p>

That works initially and displays the starting value assigned to the val variable, but it does not update when the underlying val changes.这最初有效并显示分配给val变量的起始值,但它不会在基础val更改时更新。

So I thought I would try marking the getter as tracked, but that made the output disapear:所以我想我会尝试将 getter 标记为已跟踪,但这使输出消失了:

@tracked val;

get val() {
  return this.my_obj.some.deeply.nested.path.to.val;
}

I then tried this, but it's not valid syntax:然后我尝试了这个,但它不是有效的语法:

@tracked this.my_obj.some.deeply.nested.path.to.val;

So how should this be handled in a Glimmer component?那么这应该如何在 Glimmer 组件中处理呢?

Surely the solution is not for the HTML to reference a deep path like this everytime the variable is referenced, however the new Ember docs, nice as they are, leave me none the wiser on this relatively simple/common case.当然,解决方案不是让 HTML 在每次引用变量时都引用这样的深层路径,但是新的 Ember 文档虽然很好,但在这个相对简单/常见的情况下,我没有更明智的选择。

It's simple: whatever you change needs to be @tracked .很简单:无论您更改什么,都需要@tracked If you have {{this.my_obj.some.deeply.nested.path.to.val}} in your hbs or a getter that returns that does not make a difference.如果您的hbs{{this.my_obj.some.deeply.nested.path.to.val}}或返回的 getter 没有任何区别。

But if you do this.my_obj.some.deeply.nested.path.to.val = "something" and you want the value to update, you need to ensure that on this.my_obj.some.deeply.nested.path.to val is defined as tracked.但是如果你这样做this.my_obj.some.deeply.nested.path.to.val = "something"并且你想要更新值,你需要确保在this.my_obj.some.deeply.nested.path.to val定义为已跟踪。

So this will not work:所以这行不通:

@tracked data;
constructor() {
  super(...arguments);
  this.data = { foo: 1 };
}
@action test() {
  this.data.foo = 2; // this will not correctly update
}

you need to ensure that foo is @tracked :您需要确保foo@tracked

class DataWrapper {
  @tracked foo;
}

...

@tracked data;
constructor() {
  super(...arguments);
  this.data = new DataWrapper();
  this.data.foo = 1;
}
@action test() {
  this.data.foo = 2; // this will now correctly update
}

Or you manually invalidate.或者你手动失效。 So this will work:所以这将起作用:

@tracked data;
constructor() {
  super(...arguments);
  this.data = { foo: 1 };
}
@action test() {
  this.data.foo = 2; // this will not update
  this.data = this.data; // this ensures that everything that depends on `data` will correctly update. So `foo` will correctly update.
}

Also an important thing: @tracked should never be defined on a getter.还有一件重要的事情: @tracked永远不应该在 getter 上定义。 This will not work.这是行不通的。 It should directly be defined where you want to change something that should trigger an update.它应该直接定义在您想要更改应该触发更新的内容的位置。 However you can use getters without problems.但是,您可以毫无问题地使用 getter。 It will just work, as long as everything that you set with objsomething = value is correctly @tracked .只要您使用objsomething = value设置的所有内容都是正确的@tracked ,它就会@tracked Also (pure) function calls will just work.此外(纯)函数调用也能正常工作。

There is an add-on for that.有一个附加组件。 https://www.npmjs.com/package/ember-tracked-nested which works with Ember 3.16+. https://www.npmjs.com/package/ember-tracked-nested适用于 Ember 3.16+。

Basically, it follows Lux's answer using DataWrapper and cache invalidation.基本上,它使用 DataWrapper 和缓存失效遵循 Lux 的回答。 Instead, it's done recursively with a proxy so that it doesn't matter the structure of the object or how nested it is, it will always work.相反,它是通过代理递归完成的,因此无论对象的结构或嵌套的方式如何,它都将始终有效。

import { tracked } from '@glimmer/tracking';
import Component from '@glimmer/component';
import { nested } from 'ember-tracked-nested';
import { action } from '@ember/object';

// works with POJO
export default class Foo extends Component {
  @tracked obj = nested({ bar: 2 });

  @action
  changeObj() {
      this.obj.bar = 10;
  }
}

// works when updating nested array
export default class Foo extends Component {
  @tracked obj = nested([{ bar: 2 }, { bar: 4 }]);
  
  @action
  changeObj() {
    this.obj[1].bar = 100;
  }
}

// works with array method
export default class Foo extends Component {
  @tracked obj = nested([{ bar: 2 }, { bar: 4 }]);

  @action
  changeObj() {
    this.obj.push({ bar: 6 });
  }
}

// works with POJO with getter
export default class Foo extends Component {
  @tracked obj = nested({ bar: 2, get foo() { return this.bar } });

  @action
  changeObj() {
    this.obj.bar = 9;
  }
}

I found the answer in this cheatsheet - https://ember-learn.github.io/ember-octane-vs-classic-cheat-sheet/#component-properties我在这个备忘单中找到了答案 - https://ember-learn.github.io/ember-octane-vs-classic-cheat-sheet/#component-properties

There is a @computed decorator which enables the nested attribute to be tracked for changes properly:有一个@computed装饰器,它可以正确跟踪嵌套属性的更改:

import { computed } from '@ember/object';

// Note that no 'this.' is used here
@computed("my_obj.some.deeply.nested.path.to.val")
get val() {
  return this.my_obj.some.deeply.nested.path.to.val;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM