I have a controller named MyAppWeb.ProfilesGamesController something like this
defmodule MyAppWeb.ProfilesGamesController do
use MyAppWeb, :controller
def index(conn, _params) do
games = GameModel.list_games()
render(conn, "index.json", games: games)
end
end
I have view where I’m calling like this
defmodule MyAppWeb.ProfilesGamesView do
use MyAppWeb, :view
alias MyAppWeb.ProfilesGamesView
def render("index.json", %{games: games}) do
%{data: render_many(games, ProfilesGamesView, "index.json")}
end
def render("index.json", %{game: game}) do
%{id: game.id, name: game.name}
end
end
But when I try to call my api. I’m getting this error
** (exit) an exception was raised:
** (Phoenix.Template.UndefinedError) Could not render "index.json" for MyAppWeb.ProfilesGamesView, please define a matching clause for render/2 or define a template at "lib/myappweb_web/templates/profiles_games/*". No templates were compiled for this module.
You are using profiles_games, but You pass games…
One way to catch those errors is to use a catch all clause, and inspect params.
But You can specify what should be the name to use, with as parameter. Like this
%{data: render_many(games, ProfilesGamesView, "index.json", as: :game)}
Also, use this
alias __MODULE__
# instead of
alias MyAppWeb.ProfilesGamesView
And the terminology for one game should be game.json, not index.json, but this is just cosmetic.
Even if I remove profiles from there and pass only games. Then also I’m getting the same error. I changed my controller to GamesController but still
Add a catch all clause and show the parameters You have…
def render(action, params) do
IO.inspect action
IO.inspect params
end
I have resolved that. Also I changed that to this as you said
defmodule MyAppWeb.GamesView do
use MyAppWeb, :view
alias __MODULE__
def render("index.json", %{games: games}) do
%{data: render_many(games, GamesView, "game.json")}
end
def render("game.json", %{game: game}) do
%{id: game.id, name: game.name}
end
end
You’re saying like this for this?
%{data: render_many(games, ProfilesGamesView, "index.json", as: :game)}
If You changed the name of controller and view to match game, You don’t need as parameter.
If You prefer to keep ProfilesGamesView
add as: :game.
And yes, this is how I would wrote it…
But if I use game.json like that. I’m getting this error
Request: GET /api/v1/profiles/games
** (exit) an exception was raised:
** (Phoenix.Template.UndefinedError) Could not render "game.json" for MyAppWeb.GamesView, please define a matching clause for render/2 or define a template at "lib/myapp_web/templates/games/*". No templates were compiled for this module.
Add the catch clause I mentionned, and show the parameters You get.
Yes I did that.
"index.json"
%{
conn: %Plug.Conn{
adapter: {Plug.Cowboy.Conn, :...},
assigns: %{
games: [
%MyApp.Profiles.Games.Game{
__meta__: #Ecto.Schema.Metadata<:loaded, "game">,
id: 1,
name: "siddhant",
thumbnail_url: nil,
twitch_id: nil
}
],
The catch clause should be at the end of all clauses, not at the start. You want to see why game.json fail.
Nice. So this time I got this
"game.json"
%{
games: %MyApp.Profiles.Games.Game{
__meta__: #Ecto.Schema.Metadata<:loaded, "game">,
id: 1,
name: "siddhant",
thumbnail_url: nil,
twitch_id: nil
},
view_module: MyAppWeb.GamesView,
view_template: "game.json"
}
So You see why… You expect game, but receive games.
And it’s because You don’t use normal naming. You use plural when it should be singular.
GameController
GameView
using as: :game would have solved this too.
3 Likes
You’re right actually. I changed all that to singular and it worked. Now i understand more about this.
Thanks for helping