如何根据参数设置微光组件的初始状态?
How to set initial state of glimmer component based on argument?
我正在努力弄清楚如何在微光组件层次结构中向下实现数据,向上执行操作(使用 Ember Octane,v3.15)。
我有一个包含项目列表的 parent 组件。当用户单击 Parent
组件中的按钮时,我想用相关项目的数据填充 Editor
组件;当用户在 Editor
组件中单击 "Save" 时,将更改填充回 parent。以下是实际发生的情况:
如何使文本框填充 "Hello",并在我单击 "Save" 时将更改保存回上面的列表?
代码
{{!-- 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 })
}
}
Rationale/Problem
我决定将 Editor
作为有状态组件来实现,因为这似乎是从 <Input />
组件中获取表单数据的最惯用的方式。我使用 args
设置初始状态。由于 this.currentModel
在 ParentComponent
中是 @tracked
,我希望 属性 中的 re-assignment 更新传递给 Editor
的 @currentModel
参数.
确实如此,因为单击 ParentComponent
中其中一项旁边的 "Edit" 会使 <small>Editing ID: {{this.id}}</small>
出现。但是,<Input />
元素和 id
元素的值都没有被填充。
我知道 this.text
和 this.id
没有更新,因为 EditorComponent
的 constructor
在 currentModel
时没有被 re-run parent... 的变化,但我仍然坚持该怎么做。
我试过的
当我试图解决这个问题时,我遇到了 this example (code), which has pretty much the same interaction between BlogAuthorComponent
(hbs) and 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 })
}
}
有效!但我不喜欢这个解决方案,原因如下:
- 将传递给 child 组件的东西的 属性 修改为 arg
似乎...怪异...老实说,我不确定它为什么会起作用(因为 ParentComponent#models
是 @tracked
,我不希望遵循该数组中的 POJO 属性......)
- 这会在您键入时更新 ParentComponent
中的文本,虽然整洁,但这不是我想要的——我希望仅当用户单击 "Save" 时才保留更改(在本例中什么也没做)
- 在我的真实应用程序中,当用户不是 "editing" 现有项目时,我希望表单是 "Add Item" 表单,单击 "Save" 按钮会添加一个新项目。我不确定如何在不复制表格 and/or 的情况下执行此操作,对 <Input @value
...
中的内容做一些毛茸茸的逻辑
我也遇到了,不过好像是旧版本的glimmer。
感谢您阅读到这里---如有任何建议,我将不胜感激!
要在 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 = {};
}
我正在努力弄清楚如何在微光组件层次结构中向下实现数据,向上执行操作(使用 Ember Octane,v3.15)。
我有一个包含项目列表的 parent 组件。当用户单击 Parent
组件中的按钮时,我想用相关项目的数据填充 Editor
组件;当用户在 Editor
组件中单击 "Save" 时,将更改填充回 parent。以下是实际发生的情况:
如何使文本框填充 "Hello",并在我单击 "Save" 时将更改保存回上面的列表?
代码
{{!-- 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 })
}
}
Rationale/Problem
我决定将 Editor
作为有状态组件来实现,因为这似乎是从 <Input />
组件中获取表单数据的最惯用的方式。我使用 args
设置初始状态。由于 this.currentModel
在 ParentComponent
中是 @tracked
,我希望 属性 中的 re-assignment 更新传递给 Editor
的 @currentModel
参数.
确实如此,因为单击 ParentComponent
中其中一项旁边的 "Edit" 会使 <small>Editing ID: {{this.id}}</small>
出现。但是,<Input />
元素和 id
元素的值都没有被填充。
我知道 this.text
和 this.id
没有更新,因为 EditorComponent
的 constructor
在 currentModel
时没有被 re-run parent... 的变化,但我仍然坚持该怎么做。
我试过的
当我试图解决这个问题时,我遇到了 this example (code), which has pretty much the same interaction between BlogAuthorComponent
(hbs) and 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 })
}
}
有效!但我不喜欢这个解决方案,原因如下:
- 将传递给 child 组件的东西的 属性 修改为 arg
似乎...怪异...老实说,我不确定它为什么会起作用(因为 ParentComponent#models
是 @tracked
,我不希望遵循该数组中的 POJO 属性......)
- 这会在您键入时更新 ParentComponent
中的文本,虽然整洁,但这不是我想要的——我希望仅当用户单击 "Save" 时才保留更改(在本例中什么也没做)
- 在我的真实应用程序中,当用户不是 "editing" 现有项目时,我希望表单是 "Add Item" 表单,单击 "Save" 按钮会添加一个新项目。我不确定如何在不复制表格 and/or 的情况下执行此操作,对 <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 = {};
}