Я изо всех сил пытаюсь выяснить, как реализовать данные вниз, действия вверх в иерархии компонентов мерцания (используя 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
как компонент с состоянием, потому что это казалось самым идиоматическим c способом получения данных формы из <Input />
компонента. Я установил начальное состояние, используя args
. Поскольку this.currentModel
равно @tracked
в ParentComponent
, и я ожидаю, что переназначение этого свойства обновит аргумент @currentModel
, переданный в Editor
.
Действительно, похоже, так оно и есть, поскольку при нажатии «Изменить» рядом с одним из элементов в ParentComponent
появляется <small>Editing ID: {{this.id}}</small>
. Однако ни значение элемента <Input />
, ни значение id
не заполняются.
Я понимаю, что this.text
и this.id
не обновляются, потому что constructor
из EditorComponent
не запускается повторно, когда currentModel
изменяется в родительском ... но я застрял на том, что делать вместо этого.
Что я пробовал
Когда я пытался это выяснить, я наткнулся на этот пример ( код ), который в значительной степени совпадает с взаимодействие между 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
при вводе, что, хотя и аккуратно, не то, что я хочу --- Я хочу, чтобы изменения сохранялись только тогда, когда пользователь нажимает кнопку «Сохранить» (что в данном случае ничего не делает). - В моем реальном приложении, когда пользователь не «редактирует» существующий элемент, мне бы хотелось, чтобы Форма должна быть формой «Добавить элемент», при нажатии на кнопку «Сохранить» добавляется новый элемент. Я не уверен, как это сделать, не дублируя форму и / или не делая лохматых логи c относительно того, что входит в <Input @value
...
Я также сталкивался с этим вопросом , но, похоже, это относится к старой версии glimmer.
Спасибо, что прочитали это далеко ... Буду признателен за любой совет!