How to actually define the action inside a widget

Continuing the discussion from A tour of how the Widget (Virtual DOM) code in Discourse works:

In the topic-map widget I have the below code adding an action and class name to some text in the topic-map:

contents.push(h('li.secondary', [
      h('a', { className: 'top-answer-link',
              action: 'scrollTopAnswer'}, 'Jump to Top Answer')
    ]));

The class gets added fine and I see now errors. My issue however, is that I do not know where to define the action. Usually, I would define such an action in a component. As this is a widget, where should I place this code:

actions: {
    scrollTopAnswer: function() {
      alert('scrollTopAnswer');
      $('html,body').animate({scrollTop: ($('.most_liked_post').offset().top-140)}, 'slow');
    }
  }

I use a few actions in the voting plugin. You might find a good reference here:

https://github.com/joebuhlig/discourse-feature-voting/blob/master/assets/javascripts/initializers/feature-voting.js.es6#L7

2 Likes

You can’t just add an action to an <a> element like that. You should create a new discourse widget for that:

createWidget('jump-answer', {
  tagName: 'a.top-answer-link',
  click() {
    $('html,body').animate({scrollTop: ($('.most_liked_post').offset().top-140)}, 'slow');
  }
});

Then your contents would look like this instead:

contents.push(h('li.secondary', this.attach('jump-answer')));

I should also mention that generally I prefer actions to be handled in controllers, but this is a bit of a weird case since you’re scrolling the page with jQuery. Normally you’d have the click() function call sendWidgetAction and have a method in the Controller or Route as @joebuhlig suggested.

4 Likes