Lists
Lists are used within Integral to organise data such as links, images, posts etc. Lists are most useful when they represent data that users will frequently want to change, for instance a menu (list of links) or a gallery (list of images).
Lists are managed at /admin/lists
, all users can view, create, edit and delete lists. Other features include;
- Cloning a list
- Linking objects to a list item
- Linking an image to a list item
- Setting whether or not list items can have children
- Setting a maximum amount of list items
- Organising the order of list items
How to use lists
Lists are visualised using a ListRenderer
. Pass a list to a ListRenderer and call .render
to output HTML.
The default output of the ListRenderer is an unordered list. Take for example the 3 highest grossing movies of all time;
list = Integral::List.create(title: '3 highest grossing movies of all time',
list_items: [
Integral::ListItem.create(title: 'Avatar'),
Integral::ListItem.create(title: 'Titanic'),
Integral::ListItem.create(title: 'Star Wars: The Force Awakens')
])
Integral::ListRenderer.render(list)
=> "<ul><li><a>Avatar</a></li><li><a>Titanic</a></li><li><a>Star Wars: The Force Awakens</a></li></ul>"
You can pass options to the ListRenderer, for example to change this to an ordered list;
Integral::ListRenderer.render(list, wrapper_element: 'ol')
=> "<ol><li><a>Avatar</a></li><li><a>Titanic</a></li><li><a>Star Wars: The Force Awakens</a></li></ol>")
Rather than just generic ListItems we can also add Links to lists and render them out;
list = Integral::List.create(title: '3 highest grossing movies of all time',
list_items: [
Integral::Link.create(title: 'Avatar', url: 'https://en.wikipedia.org/wiki/Avatar_(2009_film)'),
Integral::Link.create(title: 'Titanic', url: 'https://en.wikipedia.org/wiki/Titanic_(1997_film)'),
Integral::Link.create(title: 'Star Wars: The Force Awakens', url: 'https://en.wikipedia.org/wiki/Star_Wars:_The_Force_Awakens')
])
Integral::ListRenderer.render(list)
=> ""<ul><li><a href=\"https://en.wikipedia.org/wiki/Avatar_(2009_film)\">Avatar</a></li><li><a href=\"https://en.wikipedia.org/wiki/Titanic_(1997_film)\">Titanic</a></li><li><a href=\"https://en.wikipedia.org/wiki/Star_Wars:_The_Force_Awakens\">Star Wars: The Force Awakens</a></li></ul>""
That's all well and good but what if we want to create a more complicated layout for each of the items? That's where PartialListItemRenderer
comes into play. Pass this Renderer a partial view path and the output will be generated by running each item through the partial view.
Integral::ListRenderer.render(list, { wrapper_element: 'div', item_renderer: Integral::PartialListItemRenderer, item_renderer_opts: { partial_path: 'shared/movie_card' }})
=> ""<div><span>Avatar - Complex Card Partial</span><span>Titanic - Complex Card Partial</span><span>Star Wars: The Force Awakens - Complex Card Partial</span></div>
Linking objects to lists
Lists start to become really powerful when you use the PartialListItemRenderer to render list items linked to an object.
For example a user can create a list of their favourite blog posts, you can then pass that list into a ListRenderer along with a partial view path which would represent each list item.
list = Integral::List.create(title: 'Johns Top Picks',
list_items: [
Integral::Object.create( object: FactoryBot.create(:integral_post)),
Integral::Object.create( object: FactoryBot.create(:integral_post)),
Integral::Object.create( object: FactoryBot.create(:integral_post))
])
Integral::ListRenderer.render(list, { wrapper_element: 'div', html_classes: 'grid-x grid-padding-x align-center show-for-medium', item_renderer: Integral::PartialListItemRenderer, item_renderer_opts: { partial_path: 'shared/top_pick_post' }})
=> ""<div class='grid-x grid-padding-x align-center show-for-medium'><div>Avatar ...</div><div>Titanic ...</div><div>Star Wars ...</div></div>""
If a listRenderer cannot find the list or they're no list items the Renderer will output a HTML comment.
Helpers
There is a helper method render_list which can be used to easily render lists in views
render_list(Integral::List.find_by_id(12), opts)
Remember to use find_by_id
which if a record isn't found will return nil, and be handled by the renderer, rather than error out with RecordNotFound
.
Making a custom object listable
If you have created a custom object and would like Users to be able to select it as a ListItem through the UI you need to do three things;
- Mark the object as listable
acts_as_listable
- Set the listable options
# @return [Hash] listable options to be used within a RecordSelector widget
def self.listable_options
{
icon: 'rss',
record_title: I18n.t('integral.backend.record_selector.posts.record'),
selector_path: Engine.routes.url_helpers.list_backend_posts_path,
selector_title: I18n.t('integral.backend.record_selector.posts.title')
}
end
- Set list item values to represent object instances
# @return [Hash] the instance as a list item
def to_list_item
subtitle = published_at.present? ? I18n.t('integral.blog.posted_ago', time: time_ago_in_words(published_at)) : I18n.t('integral.users.status.draft')
{
id: id,
title: title,
subtitle: subtitle,
description: description,
image: image,
url: Integral::Engine.routes.url_helpers.post_url(self)
}
end
Limitations
Lists can only have items 2 levels deep.
Valid List
- List Item 1
- Child 1
- Child 2
- List Item 2
- Child 1
Invalid List
- List Item 1
- Child 1
Invalid Item
- Child 1
- List Item 2
- Child 1
Future work
Most of the future work planned for lists involves techical debt or performance improvements. Check the Wish List for more information.