ember-render-modifiers icon indicating copy to clipboard operation
ember-render-modifiers copied to clipboard

Improve the error message when `this` is undefined

Open jenweber opened this issue 5 years ago • 8 comments

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 avatar Nov 08 '19 21:11 jenweber

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

jenweber avatar Nov 08 '19 21:11 jenweber

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?

ELepolt avatar Dec 20 '19 23:12 ELepolt

@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
} ```

dgavey avatar Jan 02 '20 20:01 dgavey

#OctaneWoes. Thank you so much.

ELepolt avatar Jan 02 '20 20:01 ELepolt

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.

tniezurawski avatar Jan 15 '20 14:01 tniezurawski

@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'));
  }

dgavey avatar Jan 15 '20 14:01 dgavey

@dgavey Oh, nice! Thanks for the explanation!

tniezurawski avatar Jan 15 '20 20:01 tniezurawski

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?

mcfiredrill avatar Mar 25 '21 02:03 mcfiredrill