stimulusreflex / stimulus_reflex

Build reactive applications with the Rails tooling you already know and love.
https://docs.stimulusreflex.com
MIT License
2.28k stars 172 forks source link

Scoped onClick event #73

Closed benbonnet closed 5 years ago

benbonnet commented 5 years ago

Just found out about this wonderful gem; I'm having some issues understanding how scoping works

this example works perfectly., but given the following :

<div data-controller="example" data-reflex-root=".btn,.output">
  <a
      href="#" class='btn'
      data-count="<%= @count.to_i %>"
      data-step="1" data-reflex="click->ExampleReflex#do_stuff">
      Increment
  </a>
  <div class='output' ><%= @count.to_i %></div>
</div>

The expected change happens only once : the .output div content get from 0 to 1 at first click. Clicking further, the data-count params remains at 0. I'm having hard time understanding how to get it to work; is there an obvious error to correct ?


Going a bit further, how should things be set to make elements independent ? Given this example :

<a href="#" class='btn' data-count="<%= @count1.to_i %>" data-step="1" data-reflex="click->ExampleReflex#counter1">
  <%= @count1.to_i %>
</a>

<a href="#" data-count="<%= @count2.to_i %>" data-step="1" data-reflex="click->ExampleReflex#counter2">
  <%= @count2.to_i %>
</a>

and without further setup, a click on one element would reset the other, and vice versa. Is such approach available in the context your provide in this gem ? Is persistence the only way to go ? Does it requires extra js setup ?

thx a lot in case you have time claryfying a bit how to use it all

leastbad commented 5 years ago

Hey @benbonnet! Thanks for the question.

Do you have a repo you could share? I'd be happy to look at your code if that helps.

To my eyes and without seeing your reflex/controllers, data-reflex-root=".btn,.output" should work for the above example. The key detail is that you have to make sure that each selector resolves to one element; .btn will not work the way you want if you have multiple buttons.

In the second half of your question, I can say that it is possible and without any extra JS. Persistence is definitely required, but in this case, persistence can be the Rails session object.

Now, if you want to use individual scoping for each button, I would consider separating your design concerns from your StimulusReflex concerns. Here's my take:

<a href="#" class='btn count1' data-count="<%= @count1.to_i %>" data-step="1" data-reflex="click->ExampleReflex#counter1">
  <%= @count1.to_i %>
</a>

<a href="#" class="btn count2" data-count="<%= @count2.to_i %>" data-step="1" data-reflex="click->ExampleReflex#counter2">
  <%= @count2.to_i %>
</a>

I personally like using attributes for this sort of thing:

<div data-controller="example" data-reflex-root="[count1],[count2]">
<a href="#" class='btn' count1 data-count="<%= @count1.to_i %>" data-step="1" data-reflex="click->ExampleReflex#counter1">
  <%= @count1.to_i %>
</a>

<a href="#" class="btn" count2 data-count="<%= @count2.to_i %>" data-step="1" data-reflex="click->ExampleReflex#counter2">
  <%= @count2.to_i %>
</a>
</div>

Some people are allergic to arbitrarily defining their own attributes. Each to their own. I've been doing it with reckless abandon since 2004 and the sky has never fallen in the ways people predicted.

leastbad commented 5 years ago

Just as a footnote, there's something you could do, but I won't give an example because it's exactly what you should not actually do - and that is that you could pack all of your variables into every element. If you modified your code so that each button had data-count1 and data-count2 then you could technically get it working without any need for "persistence". But this is brittle, unworkable, ugly and you'd have to shoot yourself. Don't do this.

benbonnet commented 5 years ago

@leastbad hello thx for your reply; I'll try this all out. I've shared a private one with you (for some reasons i'd prefer to keep it that way); it is quite simple yet, just trying out an approach that i believe should get more attention. no problems with using data-attributes. I haven't introduced persistence but I guess there might already be some stuff to understand just before this part

-

btw, the example repo you provide does not showcases any app/reflexes, while it surely is what many would be interested in

leastbad commented 5 years ago

Where did you get the link for that repo? It predates StimulusReflex.

You might be excited to see this.

I'll clone your repo now.

leastbad commented 5 years ago

Hey Ben,

I've figured out what's going on and it seems like you've uncovered a subtle bug. We're working on how to fix the issue but fixing your problem is mercifully easy. All you have to do is create app/javascript/controllers/example_controller.js with the following in it:

import ApplicationController from './application_controller'

export default class extends ApplicationController {}

I do have one question for you: when I cloned your repo, application_controller.js was in app/javascript instead of app/javascript/controllers where it is supposed to live. I moved it in my local copy; can you tell me whether you moved it there (and why) or if that's where the installer/rake task put it?

benbonnet commented 5 years ago

Hey;

Thanks for your answer and the link above to the "real" repo ! I did moved application_controller.js manually, yes - I wanted to see what was the very minimal requirement and forgot to put it back to give it a go. Now I know, thx; i'll retry it soon.

Besides that, from what I can say, the generator process happened very smoothly

leastbad commented 5 years ago

I'm glad! We're still working out some of the kinks.

As for this issue with the missing example_controller.js... I'm trying to figure it out on my end. It might not be as quick a fix, and I'm trying to figure out why.

leastbad commented 5 years ago

One definite issue with your codebase is that somehow the index.js got seriously broken along the way. It's broken enough that we can't figure out how your app was doing anything at all, and in a funny way that's actually a bigger problem. Anyhow, here is what your index.js should look like unless you have a damn good reason to change it:

// Load all the controllers within this directory and all subdirectories.
// Controller files must be named *_controller.js.

import { Application } from 'stimulus'
import { definitionsFromContext } from 'stimulus/webpack-helpers'
import StimulusReflex from 'stimulus_reflex'

const application = Application.start()
const context = require.context('controllers', true, /_controller\.js$/)
application.load(definitionsFromContext(context))
StimulusReflex.initialize(application)

Still working on your specific issue, but for now feel free to update your source with the above.

leastbad commented 5 years ago

Alright, @benbonnet! I just made a PR on your project with fixes for all of your issues. Please let us know how you're doing and build cool stuff with StimulusReflex.