Skip to content

Multiple "looks" per Field #2291

Open
@ammancilla

Description

@ammancilla

What would you like to be able to do? Can you provide some examples?

As a developer I can define multiple looks for a Field and determine which one to use when using the field.

For example, I have a dashboard with three Field::HasMany attributes. For one of them I want to use the "default look" (the set of partials provided by administrate) but for the other two I want to use two different "looks" defined by me.


How could we go about implementing that?

Introduce the concept of looks (or variants?). A look is a group of partials that determine how a Field is displayed in different pages. There is a default look provided by administrate (the existing partials for each field) and developers can create, within their applications, as many custom looks as needed.

app/
├─ views/
│  ├─ fields/
│  │  ├─ belongs_to/
│  │  │  ├─ looks/
│  │  │  │  ├─ default/
│  │  │  │  │  ├─ _form.html.erb
│  │  │  │  │  ├─ _show.html.erb
│  │  │  │  │  ├─ _index.html.erb
│  │  ├─ has_many/
│  │  │  ├─ looks/
│  │  │  │  ├─ default/

...

Creating custom looks

A custom look is created by adding a new folder (named after the look) under app/views/fields/<field_name>/looks/ with the respective partials to render a Field. A custom look might define all or a subset of the three partials needed for a Field. In case of missing partials, the ones defined in the default look are used as fallback.

app/
├─ views/
│  ├─ fields/
│  │  ├─ belongs_to/
│  │  │  ├─ looks/
│  │  │  │  ├─ simple/
│  │  │  │  │  ├─ _index.html.erb
│  │  │  │  ├─ extended/
│  │  │  │  │  ├─ _show.html.erb

Using a custom look

To use a custom look, its name is passed as an option to a Field. If the option is not given, the default look is used.

  # app/dashboards/book_dashboard.rb

  ATTRIBUTE_TYPES = {
    pages: Field::HasMany,
    authors: Field::HasMany.with_options(look: :simple),
    publishers: Field::HasMany.with_options(look: :extended)
  }
  # administrate/lib/administrate/field/base.rb

  def to_partial_path
    look = options.fetch(:look, :default)
    partial_path = partial_path(page, look)
    
    if look != :default && lookup_context.exists?(partial_path, [], true)
     partial_path
    else
     partial_path(page, :default)
    end
  end

  private

  def partial_path(page, look)
    "/fields/#{self.class.field_type}/#{look}/#{page}"
  end

Can you think of other approaches to the problem?

An alternative, in my opinion less structured approach, would be to allow developers to specify the individual custom partials to be used to render a Field, through a new option (partials or so). If no custom partials are given then the default ones are used.

  • Dashboard
  # app/dashboards/book_dashboard.rb

  ATTRIBUTE_TYPES = {
    pages: Field::HasMany,
    authors: Field::HasMany.with_options(partials: { index: '/path/to/custom/partial'}),
    publishers: Field::HasMany.with_options(partials: { show: '/path/to/custom/partial'})
  }
  • Aministrate Base field
# administrate/lib/administrate/field/base.rb

def to_partial_path
  options.dig(:partials, page) || "/fields/#{self.class.field_type}/#{page}"
end

The strategy I've been using so far.

So far, whenever I need an extra "look" for a Field I create a new Field (without custom behaviour), that inherits from the respective administrate Field. Then, use the custom field in the dashboard for the attributes that need the custom look.

For example:

def HasManyExtendedView < Field::HasMany; end

def HasManySimpleView < Field::HashMany; end
  # app/dashboards/book_dashboard.rb

  ATTRIBUTE_TYPES = {
    pages: Field::HasMany,
    authors: HasManySimpleView,
    publishers: HasManyExtendedView
  }

This approach doesn't scale and results in having a lot of unnecessary files/code (a bunch of custom fields without custom behaviour)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions