Populating 2 fields in the database with a simple "<% = select (f,"

I currently have the following scenario.

I have 1 table called products with 2 columns. (name, value)
and I have 1 table called orders with 4 columns. (product, value)

My doubt is.

In my order template, I need the person to select a product and in the table automatically add the 2 fields (product, value)

I already have this.

Template (orders):

                <%= select(f, :product_id, @products_list, prompt: "product", class: "form-control") %>
                <%= error_tag f, :product_id %>

Controller(orders):

  def new(conn, _params) do
    changeset = Structure.change_request(%Request{})
    products_list = Repo.all(from(c in Product, select: {[c.name, " - R$: ", c.value], c.id}))

    render(conn, "new.html", changeset: changeset, products_list: products_list)
  end

image:
I want to remove the “valor” field, and leave only the “produto” field

Has anyone ever needed to do this?

I do it quite often. You can either massage the input data into the right format before passing to the changeset, or create a changeset function that changes the data into the two that’s needed.

@OvermindDL1
Sorry for my ignorance, but would you have an example to take away from base?

Do you have something specific that you are trying to transform? Like show the input date, how you convert it, and what you are trying to stuff it into? Like for example the current version of you save function in the OP?

So,
As you can see in my first post.
my product_list, returns the product name and only inserts the product id.
The version I am currently using saves yes, only the product ID in the product_id of another table.

The intent is:
When you click the select option, it automatically picks up the value of the product found in the product (value) table.

products_list = Repo.all(from(c in Product, select: {[c.name, " - R$: ", c.value], c.id}))

the (orders) table will have to be filled in the columns
name_product, value

I hope you haven’t confused it anymore, hehe

You are breaking database normalization rules by doing so… while it might be ok, You should be sure your db design is fine.

I normally use an intermediary table between Order and Product, something like OrderItem… to save qty for example.

But it depends on your use-case.

1 Like

In case that would be like an ajax …
But I’m avoiding using javascript, ajax to the max …
I see “liveview” will be able to help me …

But I didn’t find any examples of liveview that can help me, with select …

I’m trying to do it here, but I’m having trouble …
Anyone, good at liveview who can help me?

What I have done today …

Liveview

defmodule ProjectWeb.LiveWebhosting do
    use Phoenix.LiveView
    alias Project.Structure.Product
    alias Project.Repo
    import Ecto.Query
  
    def render(assigns) do
      ~L"""
<center><h4><%= @select_product %></h4></center>
<form phx-submit="selectproduct">
  <div class="form-group">
  <label for="formGroupExampleInput2">Hospedagem:</label>
  <select class="custom-select my-1 mr-sm-2" id="inlineFormCustomSelectPref">
    <option selected>Escolha sua Hospedagem</option>
    <option value="1">One</option>
    <option value="2">Two</option>
    <option value="3">Three</option>
  </select>
  </div>

  <div class="form-group">
    <label for="formGroupExampleInput2">Preço:</label>
    <input type="text" name="params" class="form-control" id="formGroupExampleInput2" placeholder="Valor">
  </div>

            <span class="input-group-append">
            <button type="submit" class="btn btn-info btn-flat">Salvar</button>
            </span>
</form>

      """
    end
 
    def mount(_session, socket) do
        {:ok, assign(socket, params: nil, select_product: "")}
    end

      def handle_event("selectproduct", %{"params" => params}, socket) do
        #
        products_list = Repo.all(from(c in Product, select: {[c.name, " - R$: ", c.value], c.id}))
        IO.inspect(products_list)
        # do the deploy process
        {:noreply, assign(socket, params: params, select_product: products_list)}
      end

  end

I want to make my select see my products table,
and show on screen …
as soon as you select the product, insert it into the bank

You can do that without LiveView, Phoenix Channel or Ajax.
I think @OvermindDL1 gave you a hint here:

So I would do something like below:


  def changeset(order, attrs) do
    order
    |> cast(attrs, [:product_id, :value, ...])
    |> validate_required([:product_id, ...]) # don't add :value here since we'll set it by hand later
    |> ensure_value()
  end

  defp ensure_value(
         %Ecto.Changeset{valid?: true, changes: %{product_id: p_id}} = changeset
       ) do
    put_change(changeset, :value, SomeContext.get_product(p_id).value)
  end

  defp ensure_value(changeset), do: changeset

So when the changeset is valid we get value based on on the id of the selected product and put that in the changes before creating the order.

2 Likes

@Kurisu
Very good example…
I put it as it passed …
I see that he can find the “value”.

But returns the following error.

[error] #PID<0.987.0> running ProjectWeb.Endpoint (connection #PID<0.919.0>, stream id 2) terminated
Server: localhost:4000 (http)
Request: POST /requests
** (exit) an exception was raised:
** (Ecto.ChangeError) value "19,90" for Project.Structure.Request.value in insert does not match type :integer
(ecto) lib/ecto/repo/schema.ex:931: Ecto.Repo.Schema.dump_field!/6
(ecto) lib/ecto/repo/schema.ex:940: anonymous fn/6 in Ecto.Repo.Schema.dump_fields!/5
(stdlib) maps.erl:232: :maps.fold_1/3
(ecto) lib/ecto/repo/schema.ex:938: Ecto.Repo.Schema.dump_fields!/5
(ecto) lib/ecto/repo/schema.ex:871: Ecto.Repo.Schema.dump_changes!/6
(ecto) lib/ecto/repo/schema.ex:255: anonymous fn/15 in Ecto.Repo.Schema.do_insert/4
(project) lib/project_web/controllers/request_controller.ex:32: ProjectWeb.RequestController.create/2
(project) lib/project_web/controllers/request_controller.ex:1: ProjectWeb.RequestController.action/2
(project) lib/project_web/controllers/request_controller.ex:1: ProjectWeb.RequestController.phoenix_controller_pipeline/2
(phoenix) lib/phoenix/router.ex:288: Phoenix.Router.call/2
(project) lib/project_web/endpoint.ex:1: ProjectWeb.Endpoint.plug_builder_call/2
(project) lib/plug/debugger.ex:122: ProjectWeb.Endpoint.“call (overridable 3)”/2
(project) lib/project_web/endpoint.ex:1: ProjectWeb.Endpoint.call/2
(phoenix) lib/phoenix/endpoint/cowboy2_handler.ex:42: Phoenix.Endpoint.Cowboy2Handler.init/4
(cowboy) /elixir/project/deps/cowboy/src/cowboy_handler.erl:41: :cowboy_handler.execute/2
(cowboy) /elixir/project/deps/cowboy/src/cowboy_stream_h.erl:296: :cowboy_stream_h.execute/3

I’ll have to transform to String …
I didn’t want to, but that’s ok …
will solve my problem …

Thanks so much to everyone who helped me.

1 Like