首页 > 解决方案 > 如何根据参数设置微光组件的初始状态?

问题描述

我正在努力弄清楚如何在微光组件层次结构中实现数据向下,向上操作(使用 Ember Octane,v3.15)。

我有一个带有项目列表的父组件。当用户单击Parent组件中的按钮时,我想Editor用来自相关项目的数据填充组件;当用户在组件中单击“保存”时Editor,将更改填充回父级。这是发生的事情:

我的应用程序的 GIF

如何使文本框填充“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@trackedin ParentComponent,我希望重新分配该属性以更新@currentModel传递给Editor.

确实情况似乎如此,因为单击 make 中的其中一个项目旁边的“编辑”ParentComponent出现<small>Editing ID: {{this.id}}</small>。但是,既不会填充<Input />元素的值,也不会id填充 的值。

我理解这一点this.text并且this.id没有被更新,因为当父级发生更改时不会重新运行constructorof ......但我坚持要做什么。EditorComponentcurrentModel


我试过的

当我试图弄清楚这一点时,我遇到了这个示例codeBlogAuthorComponent ),它在(hbs)和BlogAuthorEditComponenthbsjs )之间具有几乎相同的交互。他们的解决方案,适用于我的问题,是这样编写 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似乎......怪异......老实说,我不确定它为什么会起作用(因为 whileParentComponent#models@tracked,我不希望遵循该数组中 POJO 的属性...) - 这会在ParentComponent您键入时更新文本,虽然整洁,但不是我想要的 --- 我希望仅保留更改当用户点击“保存”(在这种情况下什么都不做) - 在我的真实应用程序中,当用户没有“编辑”现有项目时,我希望表单成为“添加项目”表单,点击“保存”按钮添加一个新项目。我'<Input @value

我也遇到过这个问题,但它似乎指的是旧版本的微光。

感谢您阅读这篇文章---我将不胜感激任何建议!

标签: javascriptember.jsglimmer.js

解决方案


要跟踪组件中的更改currentModeleditor设置默认值,请使用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>

请注意,尽管这会currentModel在您的parent组件中发生变异,我猜这不是您想要的。为了避免这种情况,请从您正在编辑的模型的属性中创建一个新对象。

解决方案:

// editor/component.js
export default class EditorComponent extends Component {
  get model() {
    return this.args.currentModel;
  }

  @action
  save() {
    this.args.save(this.model);
  }
}

在您的父组件中,从传递的模型创建一个新对象。currentModel另外,请记住在保存操作中重置。现在您可以检查组件的操作是否id为 null ,如果是,只需实现您的逻辑:parentsavesave

// 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 = {};
}

推荐阅读