Issues with exposing onClick event

I followed the directions shown here to expose an onClick event for a button component and a couple other components.

I am unable to get it working because whenever I create the prop during the linking, the prop gets created as a “Func object”, and not as an event handler as shown in the video. See screenshot below:

plasmic_onclick_func_object

If I try to create the prop as an Event Handler type before linking, then it doesn’t show up for selection in the link to prop menu.

Here’s a screen recording: https://www.dropbox.com/s/f4caddtjaaizli5/Desktop%202023.05.30%20-%2011.23.57.02.DVR_Trim_1.mp4?raw=1

Because the onClick gets created as Func object, when I try to attach the onclick to the instance of the component, I am unable to select/update any of its variants:

plasmic_onclick_unable_update_variant

Is there anyway to overcome this? Thanks.

That looks like a bug!

Oh, actually, the “Update variant” refers to variants of the containing component (so, if you’re in a page, and you added a Button component where you’ve exposed the onClick, and now you’re trying to attach an onClick interaction, the “update variant” is updating the variants of the containing page). If you want to update a variant of the Button component that you’re attaching the onClick to, then you need to do a few things:

  1. In the Button component, right-click the variant group that you’re trying to expose, and select “Set external access to | read and write”
  2. In the page component, in the onClick interaction, pick “Update state”, and select the Button’s state you just exposed.

Thank you! This explains a lot! Is this process documented anywhere? I read all the docs I could find and I didn’t see it anywhere.

Making this change also happened to solve another issue I had, of maintaining the states of the repeated items between tabs. Before, every time I would change tabs using the tabs container, the state changes were lost/reset. Now they are being maintained. It’s great! :slight_smile:

I notice now that when I add an onclick interaction to update state of the exposed component, the window that allows for state selection does not offer a “Switch to code” view. The only options are: “Add new data query | Add new state variable | Help”

So if I want to change the state of the “currentIndex” repeated item, I can’t, since the window forces me to pick one of the items in the repeated collection. Does that make sense? Ideally, I would want to update the state of whatever repeated item I selected.

Hmm what is your currentIndex / currentItem actually referencing here?

In this case, it would be referencing the current repeated item, which is a repeated component. It gets repeated based on an array of items (a state variable) - basically the current instance of the repeated component.

So if I have an array of tasks, and a component named “task”, which gets repeated for as many tasks are in the Tasks array, how would I add an (exposed) onclick interaction that aims to change the state of the whatever task was clicked on?

ah and say your Task has a “completed” variant you want to toggle on and off. In this case, it is more natural to bind Task.completed to the data model instead.

So for example, in your current page, you have a state $state.tasks variable with a list of Tasks, [{name: "buy milk", completed: false}], etc. You instantiate a <Task/> component, and have it repeat by $state.tasks. For each task, you set the title slot to currentItem.name, and set the completed variant to currentItem.completed. Then, you in your on click interaction, you can pick “Run code”, and set currentItem.completed = true

I see! That makes sense, but what if my array holds only the name of the tasks, and has no additional keys (like “completed”)? Say the array is simply:

["Find goat", "Milk goat", "Drink milk", "Make cheese"]

I had gone about it differently, by adding the onclick within the component, to change its state to “completed”, and it worked. But when I toggle “completed” as part of the repeated tasks, it sets all of them completed. Now I understand why it wasn’t the best way to go about it.

So if my array doesn’t have a “completed” boolean property, will your example still work?

Usually you wouldn’t want to depend on your components to derive your application model data; the model data should stand on its own, and then the components render the model data into UI. So the “completed” data needs to live somewhere; if not this array, then it’ll have to be somewhere else that your component can bind to.

However, suppose you’re trying to toggle a variant that’s not part of the application model data; for example, you’re showing a list of collapsible panels, and you want to toggle whether a panel is open or close. Then you can do it like $state.panel[currentIndex].open = true

Yes, exactly, I could do that if I could type in code :). This is why I expected to be able to switch to code view in the window where you would setup the interaction.

EDIT (months later): Although the Update state action shown in the screenshot does not allow for manual code input, the solution is as simple as choosing the Run code action instead of Update state action, where code can be entered easily.

To reply to my own question and to reinforce your points, there is no need to type in code in the window that allows for state selection, since there is no need to directly affect the change of the button/task/component.

Instead, the button/task/component should render its state by reading a model data from elsewhere (as you described).

To reword it, in case other noobs like me read this in the future, consider the following scenario:

'You wouldn’t want to force a friend to drink the glass of whiskey you’re offering just because you want him to. Instead, allow your friend to consume a glass of whiskey if one is offered."

Does that make sense?

I’ve been restructuring the app to use the data model as you suggested.

It works, however…

Since interactions are now defined as part of the page and not as part of the component, what happens if I need to use the components in different places, but with different data sources? Wouldn’t I have to recreate the interactions every time I instantiate the component?

And if I change the logic of one, I have to redo the change in the other, etc.

Isn’t that somewhat counter-productive to what I am trying to achieve?