Skip to content

Commit a42ea77

Browse files
committed
Support dot notation for :only keys in partial reloads
1 parent bbb2717 commit a42ea77

File tree

4 files changed

+103
-10
lines changed

4 files changed

+103
-10
lines changed

lib/inertia_rails/renderer.rb

Lines changed: 52 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -74,25 +74,21 @@ def merge_props(shared_data, props)
7474
end
7575

7676
def computed_props
77-
_props = merge_props(shared_data, props).select do |key, prop|
78-
if rendering_partial_component?
79-
partial_keys.none? || key.in?(partial_keys) || prop.is_a?(AlwaysProp)
80-
else
81-
!prop.is_a?(LazyProp)
82-
end
83-
end
77+
_props = merge_props(shared_data, props)
8478

85-
drop_partial_except_keys(_props) if rendering_partial_component?
79+
deep_transform_props _props do |prop, path|
80+
next [:dont_keep] unless keep_prop?(prop, path)
8681

87-
deep_transform_values _props do |prop|
88-
case prop
82+
transformed_prop = case prop
8983
when BaseProp
9084
prop.call(controller)
9185
when Proc
9286
controller.instance_exec(&prop)
9387
else
9488
prop
9589
end
90+
91+
[:keep, transformed_prop]
9692
end
9793
end
9894

@@ -105,6 +101,22 @@ def page
105101
}
106102
end
107103

104+
def deep_transform_props(props, parent_path = '', &block)
105+
props.reduce({}) do |transformed_props, (key, prop)|
106+
current_path = [parent_path, key].reject(&:empty?).join('.')
107+
108+
if prop.is_a?(Hash)
109+
nested = deep_transform_props(prop, current_path, &block)
110+
transformed_props.merge!(key => nested) unless nested.empty?
111+
else
112+
action, transformed_prop = block.call(prop, current_path)
113+
transformed_props.merge!(key => transformed_prop) if action == :keep
114+
end
115+
116+
transformed_props
117+
end
118+
end
119+
108120
def deep_transform_values(hash, &block)
109121
return block.call(hash) unless hash.is_a? Hash
110122

@@ -138,5 +150,35 @@ def resolve_component(component)
138150

139151
configuration.component_path_resolver(path: controller.controller_path, action: controller.action_name)
140152
end
153+
154+
def keep_prop?(prop, path)
155+
return true if prop.is_a?(AlwaysProp)
156+
157+
if rendering_partial_component?
158+
path_with_prefixes = path_prefixes(path)
159+
return false if excluded_by_only_partial_keys?(path_with_prefixes)
160+
return false if excluded_by_except_partial_keys?(path_with_prefixes)
161+
end
162+
163+
# Precedence: Evaluate LazyProp only after partial keys have been checked
164+
return false if prop.is_a?(LazyProp) && !rendering_partial_component?
165+
166+
true
167+
end
168+
169+
def path_prefixes(path)
170+
parts = path.split('.')
171+
(0...parts.length).map do |i|
172+
parts[0..i].join('.')
173+
end
174+
end
175+
176+
def excluded_by_only_partial_keys?(path_with_prefixes)
177+
partial_keys.present? && (path_with_prefixes & partial_keys.map(&:to_s)).empty?
178+
end
179+
180+
def excluded_by_except_partial_keys?(path_with_prefixes)
181+
partial_except_keys.present? && (path_with_prefixes & partial_except_keys.map(&:to_s)).any?
182+
end
141183
end
142184
end

spec/dummy/app/controllers/inertia_render_test_controller.rb

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,30 @@ def except_props
2424
}
2525
end
2626

27+
def deeply_nested_props
28+
render inertia: 'TestComponent', props: {
29+
flat: 'flat param',
30+
lazy: InertiaRails.lazy('lazy param'),
31+
nested_lazy: InertiaRails.lazy do
32+
{
33+
first: 'first nested lazy param',
34+
}
35+
end,
36+
nested: {
37+
first: 'first nested param',
38+
second: 'second nested param',
39+
deeply_nested: {
40+
first: 'first deeply nested param',
41+
second: false,
42+
what_about_nil: nil,
43+
deeply_nested_always: InertiaRails.always { 'deeply nested always prop' },
44+
deeply_nested_lazy: InertiaRails.lazy { 'deeply nested lazy prop' }
45+
}
46+
},
47+
always: InertiaRails.always { 'always prop' }
48+
}
49+
end
50+
2751
def view_data
2852
render inertia: 'TestComponent', view_data: {
2953
name: 'Brian',

spec/dummy/config/routes.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
get 'always_props' => 'inertia_render_test#always_props'
3434
get 'except_props' => 'inertia_render_test#except_props'
3535
get 'non_inertiafied' => 'inertia_test#non_inertiafied'
36+
get 'deeply_nested_props' => 'inertia_render_test#deeply_nested_props'
3637

3738
get 'instance_props_test' => 'inertia_rails_mimic#instance_props_test'
3839
get 'default_render_test' => 'inertia_rails_mimic#default_render_test'

spec/inertia/rendering_spec.rb

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,32 @@
111111
is_expected.to include('Brandon')
112112
end
113113
end
114+
115+
context 'with dot notation' do
116+
let(:headers) do
117+
{
118+
'X-Inertia' => true,
119+
'X-Inertia-Partial-Data' => 'nested.first,nested.deeply_nested.second,nested.deeply_nested.what_about_nil',
120+
'X-Inertia-Partial-Component' => 'TestComponent',
121+
}
122+
end
123+
124+
before { get deeply_nested_props_path, headers: headers }
125+
126+
it 'only renders the dot notated props' do
127+
expect(response.parsed_body['props']).to eq(
128+
'always' => 'always prop',
129+
'nested' => {
130+
'first' => 'first nested param',
131+
'deeply_nested' => {
132+
'second' => false,
133+
'what_about_nil' => nil,
134+
'deeply_nested_always' => 'deeply nested always prop',
135+
},
136+
},
137+
)
138+
end
139+
end
114140
end
115141

116142
context 'partial except rendering' do

0 commit comments

Comments
 (0)