Smart collection
Render a collection in a paginated, filterable table with custom row action slots or with completely customized collection rendering (e.g. cards per item instead of table rows)

bs_smart_collection(*args)

Optional options
Docs in progress! Please review the examples.

Examples

Table rendering with custom row actions

1
def response
2
#...
3
bs_smart_collection collection_config
4
#...
5
end
6
7
def collection_config
8
{
9
id: 'customers',
10
items: Customer.all,
11
paginate: 10,
12
rerender_on: "success",
13
hoverable: true,
14
columns: {
15
id: 'ID',
16
first_name: "First Name",
17
last_name: "Last Name",
18
street: "Street",
19
house_number: "House Number",
20
postal_code: "Postal Code",
21
city: "City"
22
},
23
filters: {
24
last_name: {
25
placeholder: 'Filter by Last Name',
26
match: :like,
27
}
28
},
29
slots: {
30
table_item_actions: method(:table_item_actions)
31
}
32
}
33
end
34
35
def table_item_actions customer
36
transition path: edit_dummy_customer_path(customer), delay: 300 do
37
bs_btn outline: true, size: :sm, variant: :primary, class: "m-1" do
38
bs_icon name: 'arrow-right', size: 20
39
end
40
end
41
action customer_delete_action_config(customer) do
42
bs_btn outline: true, size: :sm, variant: :danger, class: "m-1" do
43
bs_icon name: 'trash2', size: 20
44
end
45
end
46
end
47
48
def customer_delete_action_config customer
49
{
50
method: :delete,
51
path: dummy_customer_path(id: customer.id),
52
confirm: true,
53
success: {
54
emit: "success"
55
}
56
}
57
end
Copied!

Table rendering with joined model and formatted col data rendering

1
def response
2
#...
3
bs_smart_collection collection_config
4
#...
5
end
6
7
def collection_config
8
{
9
id: 'orders',
10
items: Order.includes(:customer, :order_items).all,
11
paginate: 10,
12
rerender_on: "success",
13
columns: {
14
id: 'ID',
15
'customer.last_name': {
16
heading: 'Customer Name'
17
},
18
price_in_euro: {
19
heading: 'Price in €',
20
format: -> (column_data){ "#{column_data} €" },
21
text: :end
22
}
23
},
24
filters: {
25
'customer.last_name': {
26
placeholder: 'Filter by Customer Name',
27
match: :starts_with,
28
}
29
},
30
slots: {
31
table_item_actions: method(:table_item_actions)
32
}
33
}
34
end
35
36
def table_item_actions order
37
transition path: edit_dummy_order_path(order), delay: 300 do
38
bs_btn outline: true, size: :sm, variant: :primary do
39
bs_icon name: 'arrow-right', size: 20
40
end
41
end
42
end
Copied!

Table rendering formatted col data rendering access the whole row instance object

1
def response
2
#...
3
bs_smart_collection collection_config
4
#...
5
end
6
7
def collection_config
8
{
9
id: 'orders',
10
items: Order.all,
11
paginate: 10,
12
rerender_on: "success",
13
columns: {
14
self: {
15
heading: 'Price in €',
16
format: -> (order){ "#{order.price_in_euro} €" },
17
}
18
}
19
}
20
end
Copied!

Table rendering formatted col data rendering access the whole row instance object in multiple columns

1
def response
2
#...
3
bs_smart_collection collection_config
4
#...
5
end
6
7
def collection_config
8
{
9
id: 'orders',
10
items: Order.all,
11
paginate: 10,
12
rerender_on: "success",
13
# the columns hash can only have a key once, fix by specifying the attribute name
14
columns: {
15
price_in_euro: {
16
heading: 'Price in €',
17
format: -> (column_data){ "#{column_data} €" },
18
},
19
price_in_euro_again: {
20
attribute: :price_in_euro, # fix by specifying the attribute name
21
heading: 'Price in €',
22
format: -> (column_data){ "#{column_data} €" },
23
},
24
self: {
25
heading: 'Price in €',
26
format: -> (order){ "#{order.price_in_euro} €" },
27
},
28
self_again: {
29
attribute: :self, # fix by specifying the attribute name
30
heading: 'Price in €',
31
format: -> (order){ "#{order.price_in_euro} €" },
32
}
33
}
34
}
35
end
Copied!

Table rendering via slot enabling flexible column content composition

1
def response
2
#...
3
bs_smart_collection collection_config
4
#...
5
end
6
7
def collection_config
8
{
9
id: 'orders',
10
items: Order.all,
11
paginate: 10,
12
rerender_on: "success",
13
columns: {
14
price_in_euro: {
15
heading: 'Price in € accessed via row object',
16
slot: method(:price_in_euro_column_slot) # slots ALWAYS get the whole row object passed in!
17
}
18
}
19
}
20
end
21
22
def price_in_euro_column_slot order
23
bs_badge order.price_in_euro # or whatever you want to do with all kinds of components
24
end
Copied!

Custom collection rendering

1
def response
2
#...
3
bs_smart_collection collection_config
4
#...
5
end
6
7
def collection_config
8
{
9
id: 'products',
10
items: Product.all,
11
paginate: 15,
12
rerender_on: "success",
13
filters: {
14
name: {
15
placeholder: 'Filter by Name',
16
match: :like
17
},
18
},
19
slots: {
20
collection_rendering: method(:collection_rendering)
21
}
22
}
23
end
24
25
def collection_rendering products
26
bs_row do
27
products.each do |product|
28
bs_col xl: 4, class: "mb-3" do
29
collection_card product
30
end
31
end
32
end
33
end
34
35
def collection_card product
36
bs_card title: product.name, subtitle: "#{product.price_in_euro} €", class: "h-100" do
37
paragraph product.description, class: "fw-lighter"
38
transition path: edit_dummy_product_path(product), delay: 300 do
39
bs_btn outline: true, size: :sm, variant: :primary do
40
bs_icon name: 'arrow-right', size: 20
41
end
42
end
43
action product_delete_action_config(product) do
44
bs_btn outline: true, size: :sm, variant: :danger do
45
bs_icon name: 'trash2', size: 20
46
end
47
end
48
end
49
end
50
51
def product_delete_action_config product
52
{
53
method: :delete,
54
path: dummy_product_path(id: product.id),
55
confirm: true,
56
success: {
57
emit: "success"
58
}
59
}
60
end
Copied!
Last modified 4mo ago