Render a component within a Widget. (Using select-kit components within plugin code)

This should get things working:

Will try and get it merged next week

3 Likes

Excellent progress. I have a use for this so looking forward to it :+1:t2:

2 Likes

That’s merged - let us know how you get on with it @merefield

3 Likes

Bingo! Component is now appearing in my TC-based widget, even data is populated correctly, awesome beans! :rocket:

Thanks for the amazing turnaround straddling the UK Bank Holiday!

I know that widgets handle click events, but what if my Component has an event which fires an action, where can I handle that action within a Widget?

2 Likes

Oops David, just seen an example, I’m studying it!

1 Like

Yup, that’s probably the best reference. I don’t think we’ve passed actions ‘for real’ yet in core, so shout if you run into any issues.

Looking at that test again, I suspect the actionForComponentToTrigger() function will need an @bind above it to make sure this works correctly within it.

2 Likes

OK this works to an extent:

    contents.push(
      new RenderGlimmer(
        this,
        "div.tag-chooser-component",
        hbs`<TagChooser @id="list-with-tags"  @tags={{@data.chosen}} @onChange={{action @data.onChangeUpdateTagSet}}/>
        <button onClick={{@data.actionForClick}} label="blah"/>`,
        {
          chosen: this.chosen,
          onChangeUpdateTagSet: this.onChangeUpdateTagSet,
          actionForClick: this.actionForClick
        }
      ),
    );
    return contents;
  },

  @bind
  onChangeUpdateTagSet(chosen) {
    this.chosen.push(chosen.lastObject);
  },

  @bind
  actionForClick () {
    console.log(this.chosen)
  }
});

onChangeUpdateTagSet is invoked for a change, and actionForClick is invoked on button press.

However, I’m struggling with keeping the Tag Chooser populated.

As soon as I introduce my custom action for onChange, the control fails to keep the selected tags up to date in it’s UI (even though my custom variable contains the correct values).

And if I don’t include my custom action for onChange, the components UI works again, but I can’t populate my set. :thinking:

I tried to add this.scheduleRerender() to the onChangeUpdateTagSet function but this is showing as undefined - clearly not in its scope?

I added a click event to the widget and it does then refresh showing the correct data.

However, it looks like the binding is only going one way so I can’t use the Component to update the external data, only push new data into the Component.

Is there a way to make the binding two way?

I suspect you’ll need to use widget ‘state’ rather than accessing this.chosen - widget instances are short-lived and ‘stateless’. You’ll get a new instance on each re-render, which means that this.chosen will be reset.

1 Like

this.state is undefined within the change function, so that makes things a bit tricky?

(I can pass it in, but that overwrites the data coming from the component to tell me what’s been chosen

  onChangeUpdateTagSet: this.onChangeUpdateTagSet(this.state),

)

1 Like

Are you able to share more of the widget code? IIRC for state to work you need to define a buildKey and a defaultState() method.

I’ll see if I can add add an example of this kind of thing in the MountWidget tests :eyes:

1 Like

Yeah, I have those defined. I’ll tidy and push to a repo.

A test demonstrating a form of two way binding with an embedded Component would be extremely helpful! :pray:

1 Like

I was able to access that, and the scheduleRerender() function. Updated tests and docs in

Just confirming - your @bind is imported from discourse-common/utils/decorators?

1 Like

Confirmed

Here’s the repo: GitHub - paviliondev/layouts-tag-intersection-widget: A small widget to allow you to navigate to a topic list based on a combinations of tags

And the widget file specifically: layouts-tag-intersection-widget/layouts-tag-combo.js.es6 at master · paviliondev/layouts-tag-intersection-widget (github.com)

Very much WIP and non-working as stated.

(Requires Layouts plugin installed and configured to test as is, but trivial to extract the widget code)

Ah, I think this might help:

- @onChange={{action @data.onChangeUpdateTagSet}}/>
+ @onChange=@data.onChangeUpdateTagSet/> 

When passing a ‘closure action’, there is no need for the (action helper (and in this case, I guess it actually breaks things)

2 Likes

That appears to result in TypeError: this.attrs.onChange is not a function?

But yeah, maybe that helper is masking the available attributes?

Oh my bad, we need some curlys there, otherwise it’s just parsed as a string attribute.

- @onChange={{action @data.onChangeUpdateTagSet}}/>
+ @onChange={{@data.onChangeUpdateTagSet}} /> 
1 Like

Unfortunately that results in the same error?

1 Like

Can you add a breakpoint and find out what the value of this.attrs.onChange is? (The “pause in exception” feature in the dev tools may help here)

1 Like

@David and I have resolved this over PM:

Thanks so much for your time @David.

I’ll publish the TC once complete.

3 Likes

This is now a thing, albeit slightly roughly finished atm:

image

Requires the Layouts Plugin.

Repo of Theme Component here: GitHub - paviliondev/layouts-tag-intersection-widget: A small widget to allow you to navigate to a topic list based on a combinations of tags

5 Likes