emberjs / ember-render-modifiers

Implements did-insert / did-update / will-destroy modifiers for emberjs/rfcs#415
MIT License
86 stars 33 forks source link

Improve the error message when `this` is undefined #17

Open jenweber opened 4 years ago

jenweber commented 4 years ago

If you try to use this within methods called by {{on}} and {{fn}}, they show a helpful error instructing the developer to use the @action decorator. The render modifiers could show something like that too.

Here's an example "error" use case for did-insert:

<div class="my-container" {{did-insert this.renderEditor}}></div>
import Component from '@glimmer/component';

export default class CodeEditor extends Component {
  renderEditor(el) {
    console.log(this.args.code)
  }
}

TypeError: this is undefined

{{fn}} uses this error message:

You accessed this.arg2 from a function passed to the fn helper, but the function itself was not bound to a valid this context. Consider updating to usage of @action.

jenweber commented 4 years ago

cc @rwjblue this was opened at your suggestion following some chat in octane-migration.

ELepolt commented 4 years ago

Hey all. Just came upon this today. Is there a more proper way to access this inside of a component?

// component.hbs
<Input @type="text" @value={{@token}} {{did-insert this.verifyToken @token}}/>

// component.js
verifyToken(token) {
  console.log(this) // logs 'undefined'
}

Am I misinterpreting how this addon should be used?

dgavey commented 4 years ago

@ELepolt You have to use the @action decorator for your this.verifyToken function in the component class or else this === undefined

Example


@action
verifyToken(token) {
  console.log(this) //Works
} ```
ELepolt commented 4 years ago

OctaneWoes. Thank you so much.

tniezurawski commented 4 years ago

One thing that I've learned here that someone can make use of is this.set() a value. Even with @action I've got an error: TypeError: this.set is not a function.

In my case, I had to save a reference to a node and solved that by using set from @ember/object:

{{!-- template.hbs --}}

<div {{did-insert this.setFileRef}}>
  <div id="selector"></div>
</div>
// component.js

import Component from '@glimmer/component';
- import { action } from '@ember/object';
+ import { action, set } from '@ember/object';

export default class SomeComponent extends Component {
  @action
  setFileRef() {
-    this.set('fieldRef', document.querySelector('#selector'));
+    set(this, 'fieldRef', document.querySelector('#selector'));
  }
}

That might not be the best example because in this case {{ref}} in nicer. But I'll leave it here anyway.

dgavey commented 4 years ago

@tniezurawski You don't need to use set in a glimmer component, in fact it's not recommended.

Also, you can do this code differently here. As a modifier passes in the element it's attached to so instead of looking through the whole document you can look just within this component.

@action
  setFileRef(element) {
    this.fieldRef =  element.querySelector('#selector'));
  }
tniezurawski commented 4 years ago

@dgavey Oh, nice! Thanks for the explanation!

mcfiredrill commented 3 years ago

Anyone have a hint on where I could go about adding this ? Maybe here? https://github.com/emberjs/ember-render-modifiers/blob/master/addon/modifiers/did-insert.js#L61 How could I check that it's an action and not a regular function?