Skip to content
Permalink
Branch: master
Find file Copy path
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
142 lines (112 sloc) 5.97 KB

Integral 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;

  1. Mark the object as listable
acts_as_listable
  1. 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.backend_posts_path,
      selector_title: I18n.t('integral.backend.record_selector.posts.title')
    }
  end
  1. 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
  • 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.

You can’t perform that action at this time.