Protocol ... not implemented for #Ecto.Changeset of type Ecto.Changeset (a struct)

I’m working on a CRUD application setup as an umbrella app made up of an Elixir and Phoenix apps. I’m at the point of creating a web form to allow for adding new data. I’m stuck on the error:

protocol Phoenix.HTML.FormData not implemented for #Ecto.Changeset<action: nil, changes: %{}, 
errors: [name: {"can't be blank", [validation: :required]}, description: {"can't be blank", [validation: :required]}], 
data: #Agency.Role<>, valid?: false> of type Ecto.Changeset (a struct). 
This protocol is implemented for the following type(s): Plug.Conn, Atom
  1. What does the error message mean in general?
  2. How do I fix it?

I had a similar error and after getting no where, I scrapped the app a started again. It was related to another protocol but the message worded the same. I think I’m missing some foundation and will therefore appreciate detailed answers if possible.

Below are the sections of my code that I think is relevant to my problems:

The url to the page is:

http://localhost:4000/roles/new

Router

  scope "/", AgencyWeb do
    pipe_through :browser

    get "/", PageController, :index
    resources "/rates", RateController, only: [:index]
    resources "/roles", RoleController, only: [:index, :show, :create, :new]
  end

RoleController

defmodule AgencyWeb.RoleController do
  use AgencyWeb, :controller

  def index(conn, _params) do
    roles = Agency.list_roles()
    render(conn, "index.html", roles: roles)
  end

  def new(conn, _params) do
    role = Agency.new_role()
    render(conn, "new.html", role: role)
  end

  def show(conn, _params) do
    role = Agency.new_role()
    render(conn, "show.html", role: role)
  end

  def create(conn, %{"role" => role_params}) do
    {:ok, role} = Agency.insert_role(role_params)
    redirect(conn, to: Routes.role_path(conn, :show, role))
  end
end

Role

defmodule Agency.Role do
  use Ecto.Schema
  import Ecto.Changeset

  schema "roles" do
    field(:name, :string)
    field(:description, :string)
    timestamps()
  end

  def changeset(role, params \\ %{}) do
    role
    |> cast(params, [:name, :description])
    |> validate_required([:name, :description])
    |> validate_length(:name, min: 3)
    |> validate_length(:description, min: 3, max: 200)
  end
end

Agency

defmodule Agency do
  @moduledoc """
  Main Agency app module providing access to database functions
  """
  alias Agency.{Repo, Rate, Role}
  @repo Repo

  # Role API
  def list_roles do
    @repo.all(Role)
  end

  def get_role(id) do
    @repo.get!(Role, id)
  end

  def insert_role(attrs) do
    %Role{}
    |> Role.changeset(attrs)
    |> @repo.insert()
  end

  def update_role(%Agency.Role{} = role, updates) do
    role
    |> Role.changeset(updates)
    |> @repo.update()
  end

  def new_role, do: Role.changeset(%Role{})
end

New Role Template

<h1>New Role</h1>

<%= form_for @role, Routes.role_path(@conn, :create), fn f -> %>
<%= label f, :name %>
<%= text_input f, :name %>

<%= label f, :description %>
<%= text_input f, :description %>

<div>
    <%= submit "Submit" %>
</div>
<% end %>

Thanks

3 Likes

Do you have the phoenix_ecto package as a dependency in your application?

As for what does the error mean? Are you familiar with protocols in elixir? If not, I will just quote the first sentenace from the previous link.

Protocols are a mechanism to achieve polymorphism in Elixir when you want behavior to vary depending on the data type.

It essentially allows you to define a set of functions for some type.

6 Likes

Yes I do.

I would try rm -rf _build and recompile. It seems you’re somehow missing the protocol implementation, perhaps there was a build issue?

2 Likes

Thanks @benwilson512 that fixed it.

1 Like