adopted-ember-addons / ember-paper

The Ember approach to Material Design.
https://ember-paper.netlify.app/
MIT License
888 stars 333 forks source link

PaperForm throwing backtracking assertion 3.16 + #1127

Open betocantu93 opened 4 years ago

betocantu93 commented 4 years ago

Hello, trying to upgrade to 3.16.2 but got this error, starting from 3.16.0 AFAIK.

Related PR: https://github.com/emberjs/ember.js/pull/18554

<PaperForm as |form|>
  {{#if form.isInvalid}}
    This form is invalid
  {{/if}}
  <form.input @onChange={{action (mut this.value)}} @value={{this.value}} @required={{true}}/>
</PaperForm>

Throws image

betocantu93 commented 4 years ago

A monkey-patch or quick fix is to rewrite the isInvalid and isTouched computeds to normal props that are setted by observers using scheduleOnce.

import PaperForm from "ember-paper/components/paper-form";
import { observes } from "@ember-decorators/object";
import { scheduleOnce } from "@ember/runloop";

export default class PaperFormComponent extends PaperForm {
  isInvalid = null;
  isTouched = null;

  setProp(key) {
    this.set(key, this.get("childComponents").isAny(key));
  }

  @observes("childComponents.@each.isInvalid")
  isInvalidObserver() {
    scheduleOnce("afterRender", this, this.setProp.bind(this, "isInvalid"));
  }

 @observes("childComponents.@each.isTouched")
  isTouchedObserver() {
    scheduleOnce("afterRender", this, this.setProp.bind(this, "isTouched"));
  }
}
miguelcobain commented 4 years ago

This is indeed a problem.

I'd just like to point out another workaround that is just using isInvalid afterwards, and possible using flex's order or other styling to position the invalid dependent content where you need it.

<PaperForm as |form|>
  <form.input @onChange={{action (mut this.value)}} @value={{this.value}} @required={{true}}/>
  {{#if form.isInvalid}}
    This form is invalid
  {{/if}}
</PaperForm>

Not sure how to fix this the correct way since this is, by definition, something that depends on things rendered "afterwards".

betocantu93 commented 4 years ago

Yes, I'm not sure either how to refactor this... maybe we could ask @pzuraq for advice

mtrunt commented 4 years ago

I found the same assertion error when trying to implement a similar logic to ParentMixin and ChildMixin and solved it by changing the init on the ChildMixin to didInsertElement; however this was only a 1 level deep relation (parent won't be the child of another parent).

betocantu93 commented 4 years ago

I think the init func in childMixin could have afterRender...


import { scheduleOnce } from "@ember/runloop";
export default Mixin.create({
init() {
    this._super(...arguments);
    if (this.get('parentComponent')) {
      let fn = () => {this.get('parentComponent').register(this)}
      scheduleOnce('afterRender', this, fn);
    }
  },
});
Bartheleway commented 4 years ago

Any updates on this ? For what it's worth, I think the correct way to handle all this parent/child nightmare is through events. I will try to solve this, no promises ;)