Как установить начальное состояние компонента glimmer на основе аргумента? - PullRequest
3 голосов
/ 16 февраля 2020

Я изо всех сил пытаюсь выяснить, как реализовать данные вниз, действия вверх в иерархии компонентов мерцания (используя Ember Octane, v3.15).

У меня есть родительский компонент со списком элементов. Когда пользователь нажимает кнопку внутри компонента Parent, я хочу заполнить компонент Editor данными соответствующего элемента; когда пользователь нажимает «Сохранить» в компоненте Editor, заполняет изменения обратно в родительский элемент. Вот что происходит вместо этого:

GIF of my app

Как сделать так, чтобы текстовое поле заполнялось "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 })
    }
}

enter image description here

Это работает! Но мне не нравится это решение по нескольким причинам: - Изменение свойства чего-либо, переданного дочернему компоненту как arg, кажется ... пугающим ... Я, честно говоря, не уверен, почему оно вообще работает ( поскольку ParentComponent#models равно @tracked, я бы не ожидал, что свойства POJO в этом массиве будут отслеживаться ...) - Это обновляет текст в ParentComponent при вводе, что, хотя и аккуратно, не то, что я хочу --- Я хочу, чтобы изменения сохранялись только тогда, когда пользователь нажимает кнопку «Сохранить» (что в данном случае ничего не делает). - В моем реальном приложении, когда пользователь не «редактирует» существующий элемент, мне бы хотелось, чтобы Форма должна быть формой «Добавить элемент», при нажатии на кнопку «Сохранить» добавляется новый элемент. Я не уверен, как это сделать, не дублируя форму и / или не делая лохматых логи c относительно того, что входит в <Input @value ...

Я также сталкивался с этим вопросом , но, похоже, это относится к старой версии glimmer.

Спасибо, что прочитали это далеко ... Буду признателен за любой совет!

1 Ответ

4 голосов
/ 16 февраля 2020

Чтобы отслеживать изменения в currentModel в вашем editor компоненте и установить значение по умолчанию, используйте get accessor:

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 нулевым или нет в действии save вашего компонента parent, и если это так, просто реализовать save logi c:

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