0

I am building a simple button component and I would like to test that my click handler is working by passing in console.log (or some other function) into my component. This is in Ember 4.

app/components/eu-button.hbs looks like:

<button
  type={{ this.type }}
  class={{ this.colors }}
  ...attributes
  {{on "click" (fn @onClick) }}
>
  {{#if this.args.icon }}<FaIcon @icon={{ this.args.icon }} />{{/if}}
  {{ this.args.text }}
  {{ yield }}
</button>

and implementation is:

import Component from '@glimmer/component';

export default class EuButtonComponent extends Component {
  get type() { return "button"; }
  get colors() { return "various classes"; }
}

I am calling it from my app/templates/application.hbs like this:

<EuButton @text="Test" @icon="pencil" @onClick={{ fn console.log "test" }}/>

In the hopes that I could see the console print the word "test" on a button click. However, I'm getting:

Uncaught Error: Attempted to resolve a value in a strict mode template, but that value was not in scope: console

I have tried passing in @onClick={{ fn window.console.log "test" }} and @onClick={{ fn document.console.log "test" }} with similar errors.

I think my error is more a misunderstanding of JS that Ember (or Glimmer) so I'd appreciate any help on understanding that function's scope or, alternately, a function I could use in place of console.log in this way.

GSP
  • 3,763
  • 3
  • 32
  • 54

1 Answers1

1

I don't think you can do it from the .hbs file. But if that's not your goal (which i doubt it is) then you can add a function called an action to your controller application.js (or wherever you call the component from; might be a containing component) file and there you'll be able to do whatever you want.

import Controller from '@ember/controller';
// add this decorator here
import { action } from '@ember/object';

export default class ApplicationController extends Controller {
  // here's your handler
  @action
  onClick(message) {
    console.log(message);
  }
}

then you'll be able to call it from the .hbs:

<EuButton @text="Test" @icon="pencil" @onClick={{ fn this.onClick "test" }}/>

Relevant reading: https://guides.emberjs.com/release/components/component-state-and-actions/#toc_html-modifiers-and-actions or even better https://guides.emberjs.com/release/in-depth-topics/patterns-for-actions/

Andrey Stukalin
  • 5,328
  • 2
  • 31
  • 50
  • Of course, not sure why I didn't see it like that. Thank you. – GSP May 09 '22 at 19:21
  • EDIT: I thought that would work, but when I try it the `this` is the application route and controller not the component, right? – GSP May 09 '22 at 19:28
  • Your answer made me re-think my implementation. I was caught up in trying to make the `@onClick` reference a generally available functino. I ended up use making the `@onClick` optional and, if not defined, return `console.log` and that seems to work great. – GSP May 09 '22 at 19:47
  • @GSP well, yes, the `this` would refer to the controller if you're in a route template or the component if you're in a component. Would you mind marking it as the accepted answer if it was so? – Andrey Stukalin May 10 '22 at 04:05
  • I don't think it's currently correct (though it was a huge help to me). The `this` in the ` – GSP May 10 '22 at 20:30
  • 1
    Ok, yes, you're right. Somehow i overlooked it and confused you. I'll update my answer. The handler definition should surely be in the controller (in your case), i.e. at the component caller level – Andrey Stukalin May 11 '22 at 03:49