Importing a function from SharedView

I have a function in SharedView:

def my_func1(var1) do
  "hello #{var1}"

I want to be able to call it from my html templates using only its name, without the name of the module. I’ve tried this:

# web.ex
  def view do
    quote do
      use Phoenix.View, root: "web/templates"

      # ............
      import MyApp.SharedView, only: [my_func1: 1]

but it threw an exception:

you are trying to use the module MyApp.SharedView which is currently being defined.

How should I import it properly?

Since your SharedView module also has this quote block injected, it’s trying to import itself, which is causing the error. The easiest option is to my those function, i.e. my_func/1 to another module that you import in the view block instead of defining it on SharedView

then what’s the function of SharedView?

SharedView is nothing special. It’s a view module like any other, but happens to be named Shared. You can for example use it like that in a controller: render(conn, SharedView, "partial.html", assigns) Likewise it does work in all other places where you can specify a view modules to render things.

If you want to share your posted function and not all the ability to render things just create a non-view helper module. Phoenix does that with the ErrorHelpers module.

I have a function which I’m using across many html templates. where should I put it if not in SharedView?

In Rails we have helpers for each controller, in Phoenix – Views, right?

SharedView is for shared templates. It’s not ideal to share functions in it with other views. To share function just copy what phoenix does with ErrorHelpers. There’s a ErrorHelpers module in the views folder and that module is imported in the web.ex file for views. ErrorHelpers is a plain elixir module, so you don’t have that circular import like with SharedView.

1 Like

Another option is to go ahead and use SharedView but don’t insist on importing the function and instead alias SharedView. It means all the callsites will be SharedView.my_func() but it’s more explicit and clearer to people new to the codebase where that function is defined.

The more time I spend in Elixir the more I shift from imports to aliases.

how do I alias SharedView into an html template? Where should I call “alias MyApp.SharedView”?

The templates are essentially part of the related view module, so just alias it in the view module of the template you need the function in.

1 Like

You can call it right where you were trying to import if you want it globally aliased in all views. In MyApp.Web.view: alias MyApp.SharedView

I don’t understand, say that more simply.

What’s MyApp.Web.view ?
Why would it be imported globally?

Let me describe the system:

A ‘View’ module (one that uses the web’s :view) compiles the templates in the same ‘name’ into the View module itself (specifically in ‘render/2’ functions).

So if you want something accessible from inside a template, then you make it accessible from inside a view module.

So say you have a view module named BlahView, and you have templates in a templates/blah directory, then anything you alias or import or just define straight in the BlahView will be accessible to the templates, just like if the templates where in BlahView too (because they actually are).

Usually, there is a module $MyApp.Web which has a function view/0, which returns a quoted block. Just add alias $MyApp.SharedView in the quote. You can then access that module from every view as SharedView, by adding alias $MyApp.SharedView, as: SV you can access it by using SV.

1 Like

‘If’ SharedView is a view module then you should not be importing it anywhere, rather import things in to ‘it’. If it is not a view module then you can import it just fine into other view modules.

I haven’t said “import” anywhere, also it wasn’t my idea, I was just answering @kaa.python’s question on @gregvaughn’s suggestion.

Of course I’m not a fan of importing at all, I’ve been through massive problems due to name clashing.

Aside of that, view-module, controller-module and all this stuff, is just an arbitrary distinction that helps you to avoid some boilerplate. At the end, they are just plain modules.

^ yep, that was exactly what I meant above