How to decorate models for view rendering?

I was wondering how others are decorating their models when rendering in templates.

I found this article:

…which is surprisingly useless.

To give an example: I currently have a model with a boolean field. I don’t want to display a boolean value in my view, but want to display a particular graphic specific to this model (generic helpers won’t do), and this model will be rendered in more than one view so I can’t just duplicate a helper function across my views.

Note: this is a contrived example. I likely wouldn’t use a decorator at this point, but definitely would as the views and decoration become more complicated.

What would be a good way of achieving this? I was thinking of creating a decorator module and then including that in the model, but I’d prefer to attach this at a certain point (within the view, for example), and not just attach it to the module by default.

New to Elixir so a little lost on how to go about it cleanly.

I would probably define a helper function in the view module to do the conversion, for example for a is_foo field you could have a foo_img function used as foo_img(@schema) in the template.

Schemas are just data - they don’t have any behaviour attached to them. Because of this I would argue that to a large extent talking about “decorators” as you see them in Rails doesn’t make much sense. For me decorators imply attaching behaviour to data - this is in general not possible in Elixir. That’s a big advantage and disadvantage of the functional approach.

3 Likes

True, when I say decorators I am referring to conversion. Still shaking bad habits.

This is the path I have gone down, but these models might be rendered in different views and so require that same helper. To avoid duplication, I need it in a central place i can include in different views, and I’d rather not create a massive view helpers file and pollute that with random helpers covering responsibilities for different models.

This is the current structure I have:

# web.ex
def view do
  # etc
  use MyApp.Helpers
end

defmodule MyApp.Helpers do
  defmacro __using__(_) do
    quote do
      import MyApp.LayoutHelpers
      import MyApp.SpecificModelHelpers
    end
  end

defmodule MyApp.SpecificModelHelpers do
  def foo_img(schema) do
  end
end

Does that look sensible? The only issue I see is I’m including all of my helpers in all views – though I suppose Phoenix does this, anyway (form helpers, etc).

I should note, on my travels, I did find this library which seems to be what you’re describing? –

Imo just define a view called MyModelView put the method in there and from wherever you want to display this just call out to this module directly. It’s very explicit and easy to follow and related methods stick together.

4 Likes

I do like the idea of that.

There is this library called decoratex which provides an easy way of decorating structs by adding calculated fields. I think that it may suit your requirements.

The readme contains more information and some examples.

Thanks. Kinda… but I don’t like:

defmodule Post do
  use Ecto.Schema
  use Decoratex

  decorations do
    decorate_field :happy_comments_count, :integer, &PostHelper.count_happy_comments/1
    decorate_field :troll_comments_count, :integer, &PostHelper.count_troll_comments/1
    decorate_field :mention_comments_count, :integer, &PostHelper.count_mention_comments/2, ""
    ...
  end

  schema "posts" do
    has_many :comments, Comment, on_delete: :delete_all

    field :title, :string
    field :body, :string

    add_decorations
  end
end

I don’t think it would be well-suited to view logic.

As this is a pet peeve of mine - this is not a decorator as defined in the original patterns. There a decorator sort of wraps one method and adds functionality to it retaining the name. It doesn’t define new methods for view or other purposes.

These things should be called presenters imo but that battle was lost for the majority quite long ago.

(it’s a different discussion I know but seeing that even elixir libraries carry this over is quite sad to me)

Edit:apart from nitpicking I don’t think it’s a good idea to use such a thing. Applying OO patterns to FP especially while simpler solutions exist is often not the best way to go imo. The above adds view related stuff to the core model definition. :neutral_face:

1 Like

I think michalmuskala mentioned that above. You’re right, it’s not a traditional decorator; it’s a presenter. I agree there.

What I can’t yet fully embrace is the part about not applying OO patterns to functional programming. I’ve only been FPing for 6 weeks. It’s natural to bring over patterns I’ve used in OO, and I’m sure it will take a lot longer than 6 weeks to uproot and relearn.

1 Like

A “central place” for helpers that you can “include in different views” into different modules sounds like a Module to me. The module could be imported or “use”d into different view modules.

1 Like

Yep. That’s what I’m doing. Was just confirming with the gang that I’m going in the right direction.

I’m still thinking in terms of inheritance and I really need to cut it out.

1 Like