view_component_reflex 2.3.4 → 2.3.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +332 -332
- data/app/components/view_component_reflex/component.rb +198 -198
- data/lib/view_component_reflex.rb +10 -10
- data/lib/view_component_reflex/engine.rb +36 -36
- data/lib/view_component_reflex/reflex.rb +147 -147
- data/lib/view_component_reflex/reflex_factory.rb +55 -55
- data/lib/view_component_reflex/state_adapter/memory.rb +25 -25
- data/lib/view_component_reflex/state_adapter/session.rb +24 -24
- data/lib/view_component_reflex/version.rb +3 -3
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a3ad9e3707578067e3491f47775c921657589fccb83ce3ffef79215ea104ca8d
|
4
|
+
data.tar.gz: 6aeaa003c9c4a2ba99b3aeeaf24ba49488aeb2f71279ee596ae37d9b9c727a72
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 44bc748544d32080b32d594e72c48b662b95b1f1ff82fabc74c9cb7533013e32eebf4ad2017324849d1869517669cccadda94d25eca9a0ddc605ba8c29b8a58c
|
7
|
+
data.tar.gz: 5585167d4e31a5e2d7bec04a33501adfb84f88949c86c61b7c06c56ac48fd4b9205def0cf4fb91d2fe24d43968ff52d9ea860a03adf5650907ebb68083e08a6d
|
data/README.md
CHANGED
@@ -1,332 +1,332 @@
|
|
1
|
-
# ViewComponentReflex
|
2
|
-
|
3
|
-
ViewComponentReflex allows you to write reflexes right in your view component code.
|
4
|
-
|
5
|
-
It builds upon [stimulus_reflex](https://github.com/hopsoft/stimulus_reflex) and [view_component](https://github.com/github/view_component)
|
6
|
-
|
7
|
-
## Usage
|
8
|
-
|
9
|
-
You can add reflexes to your component by adding inheriting from `ViewComponentReflex::Component`.
|
10
|
-
|
11
|
-
This will act as if you created a reflex with the method `my_cool_stuff`. To call this reflex, add `data-reflex="click->MyComponentReflex#my_cool_reflex"`, just like you're
|
12
|
-
using stimulus reflex.
|
13
|
-
|
14
|
-
ViewComponentReflex will maintain your component's instance variables between renders. You need to include `data-key=<%= key %>` on your root element, as well
|
15
|
-
as any element that stimulates a reflex. ViewComponent is inherently state-less, so the key is used to reconcile state to its respective component.
|
16
|
-
|
17
|
-
### Example
|
18
|
-
```ruby
|
19
|
-
# counter_component.rb
|
20
|
-
class CounterComponent < ViewComponentReflex::Component
|
21
|
-
def initialize
|
22
|
-
@count = 0
|
23
|
-
end
|
24
|
-
|
25
|
-
def increment
|
26
|
-
@count += 1
|
27
|
-
end
|
28
|
-
end
|
29
|
-
```
|
30
|
-
|
31
|
-
```erb
|
32
|
-
# counter_component.html.erb
|
33
|
-
<%= component_controller do %>
|
34
|
-
<p><%= @count %></p>
|
35
|
-
<%= reflex_tag :increment, :button, "Click" %>
|
36
|
-
<% end %>
|
37
|
-
```
|
38
|
-
|
39
|
-
## Collections
|
40
|
-
|
41
|
-
In order to reconcile state to components in collections, you can specify a `collection_key` method that returns some
|
42
|
-
value unique to that component.
|
43
|
-
|
44
|
-
```ruby
|
45
|
-
class TodoComponent < ViewComponentReflex::Component
|
46
|
-
def initialize(todo:)
|
47
|
-
@todo = todo
|
48
|
-
end
|
49
|
-
|
50
|
-
def collection_key
|
51
|
-
@todo.id
|
52
|
-
end
|
53
|
-
end
|
54
|
-
#
|
55
|
-
<%= render(TodoComponent.with_collection(Todo.all)) %>
|
56
|
-
```
|
57
|
-
|
58
|
-
## API
|
59
|
-
|
60
|
-
### permit_parameter?(initial_param, new_params)
|
61
|
-
If a new parameter is passed to the component during rendering, it is used instead of what's in state.
|
62
|
-
If you're storing instances in state, you can use this to properly compare them.
|
63
|
-
|
64
|
-
```ruby
|
65
|
-
def permit_parameter?(initial_param, new_param)
|
66
|
-
if new_param.instance_of? MyModel
|
67
|
-
new_param.id == @my_model.id
|
68
|
-
else
|
69
|
-
super
|
70
|
-
end
|
71
|
-
end
|
72
|
-
```
|
73
|
-
|
74
|
-
### omitted_from_state
|
75
|
-
Return an array of instance variables you want to omit from state. Only really useful if you're using the session state
|
76
|
-
adapter, and you have an instance variable that can't be serialized.
|
77
|
-
|
78
|
-
```ruby
|
79
|
-
def omitted_from_state
|
80
|
-
[:@form]
|
81
|
-
end
|
82
|
-
```
|
83
|
-
|
84
|
-
### reflex_tag(reflex, name, content_or_options_with_block = nil, options = nil, escape = true, &block)
|
85
|
-
This shares the same definition as `content_tag`, except it accepts a reflex as the first parameter.
|
86
|
-
|
87
|
-
```erb
|
88
|
-
<%= reflex_tag :increment, :button, "Click me!" %>
|
89
|
-
```
|
90
|
-
|
91
|
-
Would add a click handler to the `increment` method on your component.
|
92
|
-
|
93
|
-
To use a non-click event, specific that with `->` notation
|
94
|
-
|
95
|
-
```erb
|
96
|
-
<%= reflex_tag "mouseenter->increment", :button, "Click me!" %>
|
97
|
-
```
|
98
|
-
|
99
|
-
### reflex_data_attributes(reflex)
|
100
|
-
|
101
|
-
This helper will give you the data attributes used in the reflex_tag above if you want to build your own elements.
|
102
|
-
|
103
|
-
Build your own tag:
|
104
|
-
|
105
|
-
```erb
|
106
|
-
<%= link_to (image_tag photo.image.url(:medium)), data: reflex_data_attributes(:increment) %>
|
107
|
-
```
|
108
|
-
|
109
|
-
Render a ViewComponent
|
110
|
-
|
111
|
-
```erb
|
112
|
-
<%= render ButtonComponent.new(data: reflex_data_attributes("mouseenter->increment")) %>
|
113
|
-
```
|
114
|
-
|
115
|
-
Make sure that you assign the reflex_data_attributes to the correct element in your component.
|
116
|
-
|
117
|
-
### collection_key
|
118
|
-
If you're rendering a component as a collection with `MyComponent.with_collection(SomeCollection)`, you must define this method to return some unique value for the component.
|
119
|
-
This is used to reconcile state in the background.
|
120
|
-
|
121
|
-
```ruby
|
122
|
-
def initialize
|
123
|
-
@my_model = MyModel.new
|
124
|
-
end
|
125
|
-
|
126
|
-
def collection_key
|
127
|
-
@my_model.id
|
128
|
-
end
|
129
|
-
```
|
130
|
-
|
131
|
-
### stimulate(target, data)
|
132
|
-
Stimulate another reflex from within your component. This typically requires the key of the component you're stimulating
|
133
|
-
which can be passed in via parameters.
|
134
|
-
|
135
|
-
```ruby
|
136
|
-
def initialize(parent_key)
|
137
|
-
@parent_key = parent_key
|
138
|
-
end
|
139
|
-
|
140
|
-
def stimulate_other
|
141
|
-
stimulate("OtherComponent#method", { key: @parent_key })
|
142
|
-
end
|
143
|
-
```
|
144
|
-
|
145
|
-
### refresh!(selectors)
|
146
|
-
Refresh a specific element on the page. Using this will implicitly run `prevent_render!`.
|
147
|
-
If you want to render a specific element, as well as the component, a common pattern would be to pass `selector` as one of the parameters
|
148
|
-
|
149
|
-
```
|
150
|
-
def my_method
|
151
|
-
refresh! '#my-special-element', selector
|
152
|
-
end
|
153
|
-
```
|
154
|
-
|
155
|
-
### selector
|
156
|
-
Returns the unique selector for this component. Useful to pass to `refresh!` when refreshing custom elements.
|
157
|
-
|
158
|
-
### prevent_refresh!
|
159
|
-
By default, VCR will re-render your component after it executes your method. `prevent_refresh!` prevents this from happening.
|
160
|
-
|
161
|
-
```ruby
|
162
|
-
def my_method
|
163
|
-
prevent_refresh!
|
164
|
-
@foo = Lbar
|
165
|
-
end # the rendered page will not reflect this change
|
166
|
-
```
|
167
|
-
|
168
|
-
### refresh_all!
|
169
|
-
Refresh the entire body of the page
|
170
|
-
|
171
|
-
```ruby
|
172
|
-
def do_some_global_action
|
173
|
-
prevent_refresh!
|
174
|
-
session[:model] = MyModel.new
|
175
|
-
refresh_all!
|
176
|
-
end
|
177
|
-
```
|
178
|
-
|
179
|
-
### key
|
180
|
-
This is a key unique to a particular component. It's used to reconcile state between renders, and should be passed as a data attribute whenever a reflex is called
|
181
|
-
|
182
|
-
```erb
|
183
|
-
<button type="button" data-reflex="click->MyComponent#do_something" data-key="<%= key %>">Click me!</button>
|
184
|
-
```
|
185
|
-
|
186
|
-
### component_controller(options = {}, &blk)
|
187
|
-
This is a view helper to properly connect VCR to the component. It outputs `<div data-controller="my-controller" key=<%= key %></div>`
|
188
|
-
You *must* wrap your component in this for everything to work properly.
|
189
|
-
|
190
|
-
```erb
|
191
|
-
<%= component_controller do %>
|
192
|
-
<p><%= @count %></p
|
193
|
-
<% end %>
|
194
|
-
```
|
195
|
-
|
196
|
-
## Custom reflex base class
|
197
|
-
Reflexes typically inherit from a base ApplicationReflex. You can define the base class for a view_component_reflex by using the `reflex_base_class` method.
|
198
|
-
The parent class must inherit ViewComponentReflex::Reflex, and will throw an error if it does not.
|
199
|
-
|
200
|
-
```ruby
|
201
|
-
class ApplicationReflex < ViewComponentReflex::Reflex
|
202
|
-
|
203
|
-
end
|
204
|
-
|
205
|
-
|
206
|
-
class MyComponent < ViewComponentReflex::Component
|
207
|
-
reflex_base_class ApplicationReflex
|
208
|
-
end
|
209
|
-
```
|
210
|
-
|
211
|
-
## Common patterns
|
212
|
-
A lot of the time, you only need to update specific components when changing instance variables. For example, changing `@loading` might only need
|
213
|
-
to display a spinner somewhere on the page. You can define setters to implicitly render the appropriate pieces of dom whenever that variable is set
|
214
|
-
|
215
|
-
```ruby
|
216
|
-
def initialize
|
217
|
-
@loading = false
|
218
|
-
end
|
219
|
-
|
220
|
-
def loading=(new_value)
|
221
|
-
@loading = new_value
|
222
|
-
refresh! '#loader'
|
223
|
-
end
|
224
|
-
|
225
|
-
def do_expensive_action
|
226
|
-
prevent_refresh!
|
227
|
-
|
228
|
-
self.loading = true
|
229
|
-
execute_it
|
230
|
-
self.loading = false
|
231
|
-
end
|
232
|
-
```
|
233
|
-
|
234
|
-
```erb
|
235
|
-
<%= component_controller do %>
|
236
|
-
<div id="loader">
|
237
|
-
<% if @loading %>
|
238
|
-
<p>Loading...</p>
|
239
|
-
<% end %>
|
240
|
-
</div>
|
241
|
-
|
242
|
-
<button type="button" data-reflex="click->MyComponent#do_expensive_action" data-key="<%= key %>">Click me!</button>
|
243
|
-
<% end
|
244
|
-
```
|
245
|
-
|
246
|
-
## State
|
247
|
-
|
248
|
-
By default, view_component_reflex stores component state in memory. You can optionally set the state adapter
|
249
|
-
to use the session by changing `config.state_adapter` to `ViewComponentReflex::StateAdapter::Session`
|
250
|
-
|
251
|
-
## Custom State Adapters
|
252
|
-
|
253
|
-
ViewComponentReflex uses session for its state by default. To change this, add
|
254
|
-
an initializer to `config/initializers/view_component_reflex.rb`.
|
255
|
-
|
256
|
-
```ruby
|
257
|
-
ViewComponentReflex::Engine.configure do |config|
|
258
|
-
config.state_adapter = YourAdapter
|
259
|
-
end
|
260
|
-
```
|
261
|
-
|
262
|
-
`YourAdapter` should implement
|
263
|
-
|
264
|
-
```ruby
|
265
|
-
class YourAdapter
|
266
|
-
##
|
267
|
-
# request - a rails request object
|
268
|
-
# key - a unique string that identifies the component instance
|
269
|
-
def self.state(request, key)
|
270
|
-
# Return state for a given key
|
271
|
-
end
|
272
|
-
|
273
|
-
##
|
274
|
-
# set_state is used to modify the state.
|
275
|
-
#
|
276
|
-
# request - a rails request object
|
277
|
-
# controller - the current controller
|
278
|
-
# key - a unique string that identifies the component
|
279
|
-
# new_state - the new state to set
|
280
|
-
def self.set_state(request, controller, key, new_state)
|
281
|
-
# update the state
|
282
|
-
end
|
283
|
-
|
284
|
-
|
285
|
-
##
|
286
|
-
# store_state is used to replace the state entirely. It only accepts
|
287
|
-
# a request object, rather than a reflex because it's called from the component's
|
288
|
-
# side with the component's instance variables.
|
289
|
-
#
|
290
|
-
# request - a rails request object
|
291
|
-
# key - a unique string that identifies the component instance
|
292
|
-
# new_state - a hash containing the component state
|
293
|
-
def self.store_state(request, key, new_state = {})
|
294
|
-
# replace the state
|
295
|
-
end
|
296
|
-
end
|
297
|
-
```
|
298
|
-
|
299
|
-
|
300
|
-
## Installation
|
301
|
-
Add this line to your application's Gemfile:
|
302
|
-
|
303
|
-
```ruby
|
304
|
-
gem 'view_component_reflex'
|
305
|
-
```
|
306
|
-
|
307
|
-
And then execute:
|
308
|
-
```bash
|
309
|
-
$ bundle
|
310
|
-
```
|
311
|
-
|
312
|
-
Or install it yourself as:
|
313
|
-
```bash
|
314
|
-
$ gem install view_component_reflex
|
315
|
-
```
|
316
|
-
|
317
|
-
# Common problems
|
318
|
-
|
319
|
-
## Uninitialized constants \<component\>Reflex
|
320
|
-
A component needs to be wrapped in `<%= component_controller do %>` in order to properly initialize, otherwise the Reflex class won't get created.
|
321
|
-
|
322
|
-
## License
|
323
|
-
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
324
|
-
|
325
|
-
## Caveats
|
326
|
-
|
327
|
-
State uses session to maintain state as of right now. It also assumes your component view is written with the file extension `.html.erb`
|
328
|
-
|
329
|
-
## Support me
|
330
|
-
|
331
|
-
<a href="https://www.buymeacoffee.com/jleblanc" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/default-orange.png" alt="Buy Me A Coffee" height="40" ></a>
|
332
|
-
|
1
|
+
# ViewComponentReflex
|
2
|
+
|
3
|
+
ViewComponentReflex allows you to write reflexes right in your view component code.
|
4
|
+
|
5
|
+
It builds upon [stimulus_reflex](https://github.com/hopsoft/stimulus_reflex) and [view_component](https://github.com/github/view_component)
|
6
|
+
|
7
|
+
## Usage
|
8
|
+
|
9
|
+
You can add reflexes to your component by adding inheriting from `ViewComponentReflex::Component`.
|
10
|
+
|
11
|
+
This will act as if you created a reflex with the method `my_cool_stuff`. To call this reflex, add `data-reflex="click->MyComponentReflex#my_cool_reflex"`, just like you're
|
12
|
+
using stimulus reflex.
|
13
|
+
|
14
|
+
ViewComponentReflex will maintain your component's instance variables between renders. You need to include `data-key=<%= key %>` on your root element, as well
|
15
|
+
as any element that stimulates a reflex. ViewComponent is inherently state-less, so the key is used to reconcile state to its respective component.
|
16
|
+
|
17
|
+
### Example
|
18
|
+
```ruby
|
19
|
+
# counter_component.rb
|
20
|
+
class CounterComponent < ViewComponentReflex::Component
|
21
|
+
def initialize
|
22
|
+
@count = 0
|
23
|
+
end
|
24
|
+
|
25
|
+
def increment
|
26
|
+
@count += 1
|
27
|
+
end
|
28
|
+
end
|
29
|
+
```
|
30
|
+
|
31
|
+
```erb
|
32
|
+
# counter_component.html.erb
|
33
|
+
<%= component_controller do %>
|
34
|
+
<p><%= @count %></p>
|
35
|
+
<%= reflex_tag :increment, :button, "Click" %>
|
36
|
+
<% end %>
|
37
|
+
```
|
38
|
+
|
39
|
+
## Collections
|
40
|
+
|
41
|
+
In order to reconcile state to components in collections, you can specify a `collection_key` method that returns some
|
42
|
+
value unique to that component.
|
43
|
+
|
44
|
+
```ruby
|
45
|
+
class TodoComponent < ViewComponentReflex::Component
|
46
|
+
def initialize(todo:)
|
47
|
+
@todo = todo
|
48
|
+
end
|
49
|
+
|
50
|
+
def collection_key
|
51
|
+
@todo.id
|
52
|
+
end
|
53
|
+
end
|
54
|
+
#
|
55
|
+
<%= render(TodoComponent.with_collection(Todo.all)) %>
|
56
|
+
```
|
57
|
+
|
58
|
+
## API
|
59
|
+
|
60
|
+
### permit_parameter?(initial_param, new_params)
|
61
|
+
If a new parameter is passed to the component during rendering, it is used instead of what's in state.
|
62
|
+
If you're storing instances in state, you can use this to properly compare them.
|
63
|
+
|
64
|
+
```ruby
|
65
|
+
def permit_parameter?(initial_param, new_param)
|
66
|
+
if new_param.instance_of? MyModel
|
67
|
+
new_param.id == @my_model.id
|
68
|
+
else
|
69
|
+
super
|
70
|
+
end
|
71
|
+
end
|
72
|
+
```
|
73
|
+
|
74
|
+
### omitted_from_state
|
75
|
+
Return an array of instance variables you want to omit from state. Only really useful if you're using the session state
|
76
|
+
adapter, and you have an instance variable that can't be serialized.
|
77
|
+
|
78
|
+
```ruby
|
79
|
+
def omitted_from_state
|
80
|
+
[:@form]
|
81
|
+
end
|
82
|
+
```
|
83
|
+
|
84
|
+
### reflex_tag(reflex, name, content_or_options_with_block = nil, options = nil, escape = true, &block)
|
85
|
+
This shares the same definition as `content_tag`, except it accepts a reflex as the first parameter.
|
86
|
+
|
87
|
+
```erb
|
88
|
+
<%= reflex_tag :increment, :button, "Click me!" %>
|
89
|
+
```
|
90
|
+
|
91
|
+
Would add a click handler to the `increment` method on your component.
|
92
|
+
|
93
|
+
To use a non-click event, specific that with `->` notation
|
94
|
+
|
95
|
+
```erb
|
96
|
+
<%= reflex_tag "mouseenter->increment", :button, "Click me!" %>
|
97
|
+
```
|
98
|
+
|
99
|
+
### reflex_data_attributes(reflex)
|
100
|
+
|
101
|
+
This helper will give you the data attributes used in the reflex_tag above if you want to build your own elements.
|
102
|
+
|
103
|
+
Build your own tag:
|
104
|
+
|
105
|
+
```erb
|
106
|
+
<%= link_to (image_tag photo.image.url(:medium)), data: reflex_data_attributes(:increment) %>
|
107
|
+
```
|
108
|
+
|
109
|
+
Render a ViewComponent
|
110
|
+
|
111
|
+
```erb
|
112
|
+
<%= render ButtonComponent.new(data: reflex_data_attributes("mouseenter->increment")) %>
|
113
|
+
```
|
114
|
+
|
115
|
+
Make sure that you assign the reflex_data_attributes to the correct element in your component.
|
116
|
+
|
117
|
+
### collection_key
|
118
|
+
If you're rendering a component as a collection with `MyComponent.with_collection(SomeCollection)`, you must define this method to return some unique value for the component.
|
119
|
+
This is used to reconcile state in the background.
|
120
|
+
|
121
|
+
```ruby
|
122
|
+
def initialize
|
123
|
+
@my_model = MyModel.new
|
124
|
+
end
|
125
|
+
|
126
|
+
def collection_key
|
127
|
+
@my_model.id
|
128
|
+
end
|
129
|
+
```
|
130
|
+
|
131
|
+
### stimulate(target, data)
|
132
|
+
Stimulate another reflex from within your component. This typically requires the key of the component you're stimulating
|
133
|
+
which can be passed in via parameters.
|
134
|
+
|
135
|
+
```ruby
|
136
|
+
def initialize(parent_key)
|
137
|
+
@parent_key = parent_key
|
138
|
+
end
|
139
|
+
|
140
|
+
def stimulate_other
|
141
|
+
stimulate("OtherComponent#method", { key: @parent_key })
|
142
|
+
end
|
143
|
+
```
|
144
|
+
|
145
|
+
### refresh!(selectors)
|
146
|
+
Refresh a specific element on the page. Using this will implicitly run `prevent_render!`.
|
147
|
+
If you want to render a specific element, as well as the component, a common pattern would be to pass `selector` as one of the parameters
|
148
|
+
|
149
|
+
```
|
150
|
+
def my_method
|
151
|
+
refresh! '#my-special-element', selector
|
152
|
+
end
|
153
|
+
```
|
154
|
+
|
155
|
+
### selector
|
156
|
+
Returns the unique selector for this component. Useful to pass to `refresh!` when refreshing custom elements.
|
157
|
+
|
158
|
+
### prevent_refresh!
|
159
|
+
By default, VCR will re-render your component after it executes your method. `prevent_refresh!` prevents this from happening.
|
160
|
+
|
161
|
+
```ruby
|
162
|
+
def my_method
|
163
|
+
prevent_refresh!
|
164
|
+
@foo = Lbar
|
165
|
+
end # the rendered page will not reflect this change
|
166
|
+
```
|
167
|
+
|
168
|
+
### refresh_all!
|
169
|
+
Refresh the entire body of the page
|
170
|
+
|
171
|
+
```ruby
|
172
|
+
def do_some_global_action
|
173
|
+
prevent_refresh!
|
174
|
+
session[:model] = MyModel.new
|
175
|
+
refresh_all!
|
176
|
+
end
|
177
|
+
```
|
178
|
+
|
179
|
+
### key
|
180
|
+
This is a key unique to a particular component. It's used to reconcile state between renders, and should be passed as a data attribute whenever a reflex is called
|
181
|
+
|
182
|
+
```erb
|
183
|
+
<button type="button" data-reflex="click->MyComponent#do_something" data-key="<%= key %>">Click me!</button>
|
184
|
+
```
|
185
|
+
|
186
|
+
### component_controller(options = {}, &blk)
|
187
|
+
This is a view helper to properly connect VCR to the component. It outputs `<div data-controller="my-controller" key=<%= key %></div>`
|
188
|
+
You *must* wrap your component in this for everything to work properly.
|
189
|
+
|
190
|
+
```erb
|
191
|
+
<%= component_controller do %>
|
192
|
+
<p><%= @count %></p
|
193
|
+
<% end %>
|
194
|
+
```
|
195
|
+
|
196
|
+
## Custom reflex base class
|
197
|
+
Reflexes typically inherit from a base ApplicationReflex. You can define the base class for a view_component_reflex by using the `reflex_base_class` method.
|
198
|
+
The parent class must inherit ViewComponentReflex::Reflex, and will throw an error if it does not.
|
199
|
+
|
200
|
+
```ruby
|
201
|
+
class ApplicationReflex < ViewComponentReflex::Reflex
|
202
|
+
|
203
|
+
end
|
204
|
+
|
205
|
+
|
206
|
+
class MyComponent < ViewComponentReflex::Component
|
207
|
+
reflex_base_class ApplicationReflex
|
208
|
+
end
|
209
|
+
```
|
210
|
+
|
211
|
+
## Common patterns
|
212
|
+
A lot of the time, you only need to update specific components when changing instance variables. For example, changing `@loading` might only need
|
213
|
+
to display a spinner somewhere on the page. You can define setters to implicitly render the appropriate pieces of dom whenever that variable is set
|
214
|
+
|
215
|
+
```ruby
|
216
|
+
def initialize
|
217
|
+
@loading = false
|
218
|
+
end
|
219
|
+
|
220
|
+
def loading=(new_value)
|
221
|
+
@loading = new_value
|
222
|
+
refresh! '#loader'
|
223
|
+
end
|
224
|
+
|
225
|
+
def do_expensive_action
|
226
|
+
prevent_refresh!
|
227
|
+
|
228
|
+
self.loading = true
|
229
|
+
execute_it
|
230
|
+
self.loading = false
|
231
|
+
end
|
232
|
+
```
|
233
|
+
|
234
|
+
```erb
|
235
|
+
<%= component_controller do %>
|
236
|
+
<div id="loader">
|
237
|
+
<% if @loading %>
|
238
|
+
<p>Loading...</p>
|
239
|
+
<% end %>
|
240
|
+
</div>
|
241
|
+
|
242
|
+
<button type="button" data-reflex="click->MyComponent#do_expensive_action" data-key="<%= key %>">Click me!</button>
|
243
|
+
<% end
|
244
|
+
```
|
245
|
+
|
246
|
+
## State
|
247
|
+
|
248
|
+
By default, view_component_reflex stores component state in memory. You can optionally set the state adapter
|
249
|
+
to use the session by changing `config.state_adapter` to `ViewComponentReflex::StateAdapter::Session`
|
250
|
+
|
251
|
+
## Custom State Adapters
|
252
|
+
|
253
|
+
ViewComponentReflex uses session for its state by default. To change this, add
|
254
|
+
an initializer to `config/initializers/view_component_reflex.rb`.
|
255
|
+
|
256
|
+
```ruby
|
257
|
+
ViewComponentReflex::Engine.configure do |config|
|
258
|
+
config.state_adapter = YourAdapter
|
259
|
+
end
|
260
|
+
```
|
261
|
+
|
262
|
+
`YourAdapter` should implement
|
263
|
+
|
264
|
+
```ruby
|
265
|
+
class YourAdapter
|
266
|
+
##
|
267
|
+
# request - a rails request object
|
268
|
+
# key - a unique string that identifies the component instance
|
269
|
+
def self.state(request, key)
|
270
|
+
# Return state for a given key
|
271
|
+
end
|
272
|
+
|
273
|
+
##
|
274
|
+
# set_state is used to modify the state.
|
275
|
+
#
|
276
|
+
# request - a rails request object
|
277
|
+
# controller - the current controller
|
278
|
+
# key - a unique string that identifies the component
|
279
|
+
# new_state - the new state to set
|
280
|
+
def self.set_state(request, controller, key, new_state)
|
281
|
+
# update the state
|
282
|
+
end
|
283
|
+
|
284
|
+
|
285
|
+
##
|
286
|
+
# store_state is used to replace the state entirely. It only accepts
|
287
|
+
# a request object, rather than a reflex because it's called from the component's
|
288
|
+
# side with the component's instance variables.
|
289
|
+
#
|
290
|
+
# request - a rails request object
|
291
|
+
# key - a unique string that identifies the component instance
|
292
|
+
# new_state - a hash containing the component state
|
293
|
+
def self.store_state(request, key, new_state = {})
|
294
|
+
# replace the state
|
295
|
+
end
|
296
|
+
end
|
297
|
+
```
|
298
|
+
|
299
|
+
|
300
|
+
## Installation
|
301
|
+
Add this line to your application's Gemfile:
|
302
|
+
|
303
|
+
```ruby
|
304
|
+
gem 'view_component_reflex'
|
305
|
+
```
|
306
|
+
|
307
|
+
And then execute:
|
308
|
+
```bash
|
309
|
+
$ bundle
|
310
|
+
```
|
311
|
+
|
312
|
+
Or install it yourself as:
|
313
|
+
```bash
|
314
|
+
$ gem install view_component_reflex
|
315
|
+
```
|
316
|
+
|
317
|
+
# Common problems
|
318
|
+
|
319
|
+
## Uninitialized constants \<component\>Reflex
|
320
|
+
A component needs to be wrapped in `<%= component_controller do %>` in order to properly initialize, otherwise the Reflex class won't get created.
|
321
|
+
|
322
|
+
## License
|
323
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
324
|
+
|
325
|
+
## Caveats
|
326
|
+
|
327
|
+
State uses session to maintain state as of right now. It also assumes your component view is written with the file extension `.html.erb`
|
328
|
+
|
329
|
+
## Support me
|
330
|
+
|
331
|
+
<a href="https://www.buymeacoffee.com/jleblanc" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/default-orange.png" alt="Buy Me A Coffee" height="40" ></a>
|
332
|
+
|