17
votes

I'm trying to display a form on the screen. But I keep getting this error when I try to start the server. locations_controller.ex == ** (CompileError) web/controllers/locations_controller.ex:5: Locations.__struct__/1 is undefined, cannot expand struct Locations. BTW I'm new to elixir so I'm probably doing something really obvious wrong.

Here is my code:

locations.controller.ex

 def new(conn, _params) do
    changeset = Locations.changeset(%Locations{})

    render conn, "new.html", changeset: changeset
  end

  def create(conn, %{"locations" => %{ "start" => start, "end" => finish }}) do
    changeset = %AwesomeLunch.Locations{start: start, end: finish}
    Repo.insert(changeset)

    redirect conn, to: locations_path(conn, :index)
  end

VIEW

<h1>Hey There</h1>

<%= form_for @changeset, locations_path(@conn, :create), fn f -> %>

  <label>
    Start: <%= text_input f, :start %>
  </label>

  <label>
    End: <%= text_input f, :end %>
  </label>

  <%= submit "Pick An Awesome Lunch" %>

<% end %>

model

    defmodule AwesomeLunch.Locations do
  use AwesomeLunch.Web, :model

  use Ecto.Schema
  import Ecto.Changeset

  schema "locations" do
    field :start
    field :end
  end

  def changeset(struct, params \\ %{}) do
    struct
    |> cast(params, [:start, :end])
    |> validate_required([:start, :end])
  end
end

Like I said I'm getting this error:

    locations_controller.ex ==
** (CompileError) web/controllers/locations_controller.ex:5: Locations.__struct__/1 is undefined, cannot expand struct Locations
3
Not directly relevant to resolving the problem / question, but as I understand it, a Phoenix best practice is to name your domain objects in the singular rather than plural (eg. defmodule AwesomeLunch.Location, LocationController, etc.). That way you don't have the confusing conventions of sometimes referring to singular and sometimes referring to plural, like in the Rails world.Topher Hunt

3 Answers

21
votes

Modules in Elixir need to be referred to by their full name or an alias of it. You can either change all Locations to AwesomeLunch.Locations, or if you want to use a shorter name, you can call alias in that module:

defmodule AwesomeLunch.LocationsController do
  alias AwesomeLunch.Locations

  ...
end
2
votes

I am developing an umbrella project and I was getting the same error some times.

If you create a struct declared in App1, and want to use it in App2 you have to add App1 to App2 as dependency. If you don't do this and If App2 loaded before App1 the error occurs.

Example: {:app1, in_umbrella: true}

0
votes

I had the same error and for me it worked configuring the controller, this way:

defmodule AwesomeLunch.LocationsController do
  use AwesomeLunch.Web, :controller

  alias AwesomeLunch.Locations

  ...
end