[英]How to set initial state of child component based on parent component's API call?
[英]How to set initial state of glimmer component based on argument?
我正在努力弄清楚如何在微光组件层次结构中实现数据向下,向上操作(使用 Ember Octane,v3.15)。
我有一个带有项目列表的父组件。 当用户单击Parent
组件中的按钮时,我想用相关项目的数据填充Editor
组件; 当用户在Editor
组件中单击“保存”时,将更改填充回父级。 这是发生的事情:
如何使文本框填充“Hello”,并在单击“保存”时将更改保留回上面的列表?
{{!-- app/components/parent.hbs --}}
<ul>
{{#each this.models as |model|}}
<li>{{model.text}} <button {{on 'click' (fn this.edit model)}}>Edit</button></li>
{{/each}}
</ul>
<Editor @currentModel={{this.currentModel}} @save={{this.save}} />
// app/components/parent.js
import Component from '@glimmer/component';
export default class ParentComponent extends Component {
@tracked models = [
{ id: 1, text: 'Hello'},
{ id: 2, text: 'World'}
]
@tracked currentModel = null;
@action
edit(model) {
this.currentModel = model;
}
@action
save(model) {
// persist data
this.models = models.map( (m) => m.id == model.id ? model : m )
}
}
{{!-- app/components/editor.hbs --}}
{{#if @currentModel}}
<small>Editing ID: {{this.id}}</small>
{{/if}}
<Input @value={{this.text}} />
<button {{on 'click' this.save}}>Save</button>
// app/components/editor.hbs
import Component from '@glimmer/component';
import { tracked } from "@glimmer/tracking";
import { action } from "@ember/object";
export default class EditorComponent extends Component {
@tracked text;
@tracked id;
constructor() {
super(...arguments)
if (this.args.currentModel) {
this.text = this.args.currentModel.text;
this.id = this.args.currentModel.id;
}
}
@action
save() {
// persist the updated model back to the parent
this.args.save({ id: this.id, text: this.text })
}
}
我决定将Editor
实现为有状态组件,因为这似乎是从<Input />
组件中获取表单数据的最惯用方式。 我使用args
设置初始状态。 由于this.currentModel
在ParentComponent
中是@tracked
,我希望重新分配该属性以更新传递给Editor
的@currentModel
参数。
确实情况似乎如此,因为单击ParentComponent
中的一项旁边的“编辑”会使<small>Editing ID: {{this.id}}</small>
出现。 但是,既没有填充<Input />
元素的值,也没有填充id
。
我知道this.text
和this.id
没有被更新,因为当父级中的currentModel
更改时, EditorComponent
的constructor
没有重新运行......但我被困在该怎么做。
当我试图弄清楚这一点时,我遇到了这个示例( code ),它在BlogAuthorComponent
( hbs )和BlogAuthorEditComponent
( hbs , js )之间具有几乎相同的交互。 他们的解决方案,适用于我的问题,是这样编写 EditorComponent :
{{!-- app/components/editor.hbs --}}
{{#if this.isEditing}}
<small>Editing ID: {{@currentModel.id}}</small>
<Input @value={{@currentModel.text}} />
<button {{on 'click' this.save}}>Save</button>
{{/if}}
// app/components/editor.hbs
import Component from '@glimmer/component';
import { tracked } from "@glimmer/tracking";
import { action } from "@ember/object";
export default class EditorComponent extends Component {
get isEditing() {
return !!this.args.currentModel
}
@action
save() {
// persist the updated model back to the parent
this.args.save({ id: this.id, text: this.text })
}
}
有用! 但我不喜欢这个解决方案,有几个原因:
arg
传递给子组件的某些东西的属性似乎......令人毛骨悚然......老实说,我不确定它为什么会起作用(因为虽然ParentComponent#models
是@tracked
,但我不希望 POJO 的属性在要遵循的数组中...)ParentComponent
中的文本,虽然整洁,但不是我想要的——我希望只有在用户单击“保存”时才保留更改(在这种情况下什么都不做)<Input @value
...我也遇到过这个问题,但它似乎指的是旧版本的微光。
感谢您阅读这篇文章---我将不胜感激任何建议!
要在editor
组件中跟currentModel
的更改并设置默认值,请使用get
访问器:
get model() {
return this.args.currentModel || { text: '', id: null };
}
在您的模板中执行以下操作:
{{#if this.model.id}}
<small>
Editing ID:
{{this.model.id}}
</small>
{{/if}}
<Input @value={{this.model.text}} />
<button type="button" {{on "click" this.save}}>
Save
</button>
请注意,这会改变您parent
组件中的currentModel
,我猜这不是您想要的。 为了避免这种情况,请从您正在编辑的模型的属性中创建一个新对象。
解决方案:
// editor/component.js
export default class EditorComponent extends Component {
get model() {
return this.args.currentModel;
}
@action
save() {
this.args.save(this.model);
}
}
在您的父组件中,从传递的模型创建一个新对象。 另外,请记住在保存操作中重置currentModel
。 现在您可以在parent
组件的save
操作中检查id
是否为空,如果是,只需实现您的save
逻辑:
// parent/component.js
@tracked currentModel = {};
@action
edit(model) {
// create a new object
this.currentModel = { ...model };
}
@action
save(model) {
if (model.id) {
this.models = this.models.map((m) => (m.id == model.id ? model : m));
} else {
// save logic
}
this.currentModel = {};
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.