Recognize new params in the template

Friends, I just created a submodule in the controller.

  def filter_ecdr(conn, params) do
    filter = Structure.list_cdr
    |> where([c], fragment("? BETWEEN ? AND ?", c.calldate, params["campo1"], params["campo2"]))
    |> order_by([c], desc: c.calldate)
    |> Repo.paginate(params)

    render(conn, "index.html", filter: filter.entries, filter: filter)
  end

How do I, for my template to recognize these variables:
campo1 and campo2?

Ex template:

  <div class="input-group-prepend">
    <div class="input-group-text"><i class="fas fa-calendar-alt"></i></div>
    </div>
    <input type="date" class="form-control" id="campo1" placeholder="Data">
  </div>

Erro:


== Compilation error in file lib/gcall_web/controllers/ecdr_controller.ex ==
** (Ecto.Query.CompileError) `params["date_start"]` is not a valid query expression. If you want to invoke Access.get/2 in a query, make sure that the module Access is required and that get/2 is a macro

    (ecto) expanding macro: Ecto.Query.where/3
    (gcall) lib/gcall_web/controllers/ecdr_controller.ex:74: GcallWeb.EcdrController.filter_ecdr/2
    (ecto) expanding macro: Ecto.Query.order_by/3
    (gcall) lib/gcall_web/controllers/ecdr_controller.ex:75: GcallWeb.EcdrController.filter_ecdr/2
    (elixir) expanding macro: Kernel.|>/2
    (gcall) lib/gcall_web/controllers/ecdr_controller.ex:76: GcallWeb.EcdrController.filter_ecdr/2
    (elixir) lib/kernel/parallel_compiler.ex:208: anonymous fn/4 in Kernel.ParallelCompiler.spawn_workers/6

I’m not sure how your template is related… Its your query that is wrong…

I’d extract both params via pattern matching and then pin them in the query, hoping that I’m not doing the next error (I have not yet worked much with ecto, but I am remembering that variables from the outside need to be pinned in a query):

def filter_ecdr(conn, %{"campo1" => campo1, "campo2" => campo2}) do
  filter = Structure.list_cdr()
    |> where([c], fragment("? BETWEEN ? AND ?", c.calldate, ^campo1, ^campo2))
    |> order_by([c], desc: c.calldate)
    |> Repo.paginate(params)

  render(conn, "index.html", filter: filter.entries, filter: filter)
end

PS, aside of that, while typing this out, why have you assigned @filter to two different values? It can have only one of those…

You don’t have the name attribute in your html input tag (perhaps you confused it with the id…you actually need name):

<input name="campo1" type="date" class="form-control" id="campo1" placeholder="Data">

I would suggest you use the helpers provided by Phoenix. Here’s the documentation for the text_input:
https://hexdocs.pm/phoenix_html/Phoenix.HTML.Form.html#text_input/3

But, you would still have to extract the params before using them in a query as @NobbZ suggested above.

Really @sfusato
I just forgot to add the name field to the template.
I removed the old one
<input type="date" class="form-control" id="campo1" placeholder="Data">

I added the new one
<%= date_input(:calldate, :campo1, class: "form-control", id: "campo1", placeholder: "Data") %>

@NobbZ

def filter_ecdr(conn, %{"campo1" => campo1, "campo2" => campo2}) do
  filter = Structure.list_cdr()
    |> where([c], fragment("? BETWEEN ? AND ?", c.calldate, ^campo1, ^campo2))
    |> order_by([c], desc: c.calldate)
    |> Repo.paginate(params)

  render(conn, "index.html", filter: filter.entries, filter: filter)
end

I added it to the controller, just like you did.
Now I see that he can pull the value.

iex(22)> [info] GET /relatorios
[debug] Processing with GcallWeb.EcdrController.index/2
  Parameters: %{"calldate" => %{"campo2" => "2019-06-08", "campo1" => "2019-06-01"}}
  Pipelines: [:browser]
[debug] QUERY OK source="cdr" db=9.8ms
SELECT count('*') FROM `cdr` AS c0 []
[debug] QUERY OK source="cdr" db=1.2ms
SELECT c0.`calldate`, c0.`accountcode`, c0.`amaflags`, c0.`billsec`, c0.`channel`, c0.`clid`, c0.`dcontext`, c0.`disposition`, c0.`dst`, c0.`dstchannel`, c0.`duration`, c0.`lastapp`, c0.`lastdata`, c0.`src`, c0.`uniqueid`, c0.`userfield` FROM `cdr` AS c0 ORDER BY c0.`calldate` DESC LIMIT ? OFFSET ? [10, 0]
[info] Sent 200 in 12ms
[info] Replied phoenix:live_reload :ok

I added it to the controller, just like you did.
Now I see that he can pull the value.

But by clicking the button in the template
<% = submit "Browse"%>

It simply refreshes the page, instead of using this filter_ecdr

Your params are inside calldate map.

Change this:

def filter_ecdr(conn, %{"campo1" => campo1, "campo2" => campo2}) do

to this:

def filter_ecdr(conn, %{"calldate" => %{"campo1" => campo1, "campo2" => campo2}}) do

@sfusato I made this change,
more without success.

It does not generate error.
Note in the debug, that when loading the page, it returns the “index” inves of the filter_ecdr

iex(50)> [info] GET /relatorios
[debug] Processing with GcallWeb.EcdrController.index/2
Parameters: %{“calldate” => %{“campo2” => “2019-06-09”, “campo1” => “2019-06-01”}}
Pipelines: [:browser]
[debug] QUERY OK source=“cdr” db=13.6ms
SELECT count(‘*’) FROM cdr AS c0
[debug] QUERY OK source=“cdr” db=0.6ms
SELECT c0.calldate, c0.accountcode, c0.amaflags, c0.billsec, c0.channel, c0.clid, c0.dcontext, c0.disposition, c0.dst, c0.dstchannel, c0.duration, c0.lastapp, c0.lastdata, c0.src, c0.uniqueid, c0.userfield FROM cdr AS c0 ORDER BY c0.calldate DESC LIMIT ? OFFSET ? [10, 0]
[info] Sent 200 in 15ms
[info] Replied phoenix:live_reload :ok

What does your form look like? Especially the action? And where does the route of that URL point to?

With this, you are basically looking for a date that is between the range [c.calldate, campo1] and campo2 is supposed to be a boolean?

Is this correct? Perhaps the range is supposed to be between campo1 and campo2 instead and c.calldate is the boolean.

Google says that’s the correct syntax for the between operation.

1 Like

You’re right.

[debug] QUERY OK source=“cdr” db=13.6ms
SELECT count(’*’) FROM cdr AS c0
[debug] QUERY OK source=“cdr” db=0.6ms
SELECT c0. calldate , c0. accountcode , c0. amaflags , c0. billsec , c0. channel , c0. clid , c0. dcontext , c0. disposition , c0. dst , c0. dstchannel , c0. duration , c0. lastapp , c0. lastdata , c0. src , c0. uniqueid , c0. userfield FROM cdr AS c0 ORDER BY c0. calldate DESC LIMIT ? OFFSET ? [10, 0]

The where clause got dropped from the final query for some reason. The ORDER BY is there, the pagination is there though.

As you can see in the posted logs, the index action is called, that’s why I’m asking for the form and routes details.

I found out, it was missing

Map.put (assigns,: action …

Correct me if I’m wrong,

Route

 resources "/relat", EcdrController
  get "/relat/:id", EcdrController, :filter_ecdr
  post "/relat/:id", EcdrController, :filter_ecdr

First Test:

Template:

Before
index.html
<%= render "filter.html" %>

After
<%= render "filter.html", Map.put(assigns, :action, Routes.ecdr_path(@conn, :filter_ecdr)) %>

filter.html

 <%= form_for @conn, @action, fn f -> %>
 <%= text_input f, (:calldate, :campo1, class: "form-control", id: "inlineFormInputGroupDatestart", placeholder: "Data") %>
 <%= text_input f, (:calldate, :campo2, class: "form-control", id: "inlineFormInputGroupDatestart", placeholder: "Data") %>
<% end %>

But now I get this error:

== Compilation error in file lib/gcall_web/views/ecdr_view.ex ==
** (CompileError) lib/gcall_web/views/ecdr_view.ex:64: undefined function text_input/4
    (elixir) src/elixir_locals.erl:107: :elixir_locals."-ensure_no_undefined_local/3-lc$^0/1-0-"/2
    (elixir) src/elixir_locals.erl:107: anonymous fn/3 in :elixir_locals.ensure_no_undefined_local/3
    (stdlib) erl_eval.erl:680: :erl_eval.do_apply/6
    (elixir) lib/kernel/parallel_compiler.ex:208: anonymous fn/4 in Kernel.ParallelCompiler.spawn_workers/6

Second Test:

Template
filter.html.eex

<%= form_for @conn, Routes.ecdr_path(@conn, :filter_ecdr), [method: :get], fn f -> %>
<%= date_input(f, :campo1, class: "form-control", id: "inlineFormInputGroupDatestart", placeholder: "Data") %>
<%= date_input(f, :campo2, class: "form-control", id: "inlineFormInputGroupDatestart", placeholder: "Data") %>
<% end %>

It does not generate any error, when clicking on submit.
Just refresh the page

[info] GET /relat
[debug] Processing with GcallWeb.EcdrController.index/2
Parameters: %{“_utf8” => “✓”, “campo2” => “2019-06-08”, “campo1” => “2019-06-01”, “dcontext” => %{“contexto” => “”}, “disposition” => %{“status” => “Todas”}, “dst” => %{“destino” => “”}, “src” => %{“origem” => “”}}
Pipelines: [:browser]
[debug] QUERY OK source=“cdr” db=11.5ms
SELECT count(‘*’) FROM cdr AS c0
[debug] QUERY OK source=“cdr” db=0.3ms
SELECT c0.calldate, c0.accountcode, c0.amaflags, c0.billsec, c0.channel, c0.clid, c0.dcontext, c0.disposition, c0.dst, c0.dstchannel, c0.duration, c0.lastapp, c0.lastdata, c0.src, c0.uniqueid, c0.userfield FROM cdr AS c0 ORDER BY c0.calldate DESC LIMIT ? OFFSET ? [10, 0]
[info] Sent 200 in 12ms
[info] Replied phoenix:live_reload :ok

This is not a valid function call (well it is, just will not do what you expect, parenthesis are turning the inner bit into an expression of some form)?

No its not, it causes a syntax error:

iex(1)> quote do
...(1)> text_input f, (:calldate, :campo1, class: "form-control", id: "inlineFormInputGroupDatestart", placeholder: "Data")
...(1)> end
** (SyntaxError) iex:2: syntax error before: ')'
1 Like

Thought that was the case but that wouldn’t match the error they gave, so the only thing I can guess is that the posted code isn’t the actual code that caused that error. This is why SSCCE’s are so so very important. ^.^

1 Like

Basically I had the template, totally wrong.

Old:

<%= text_input f, (:calldate, :campo1, class: "form-control", id: "inlineFormInputGroupDatestart", placeholder: "Data") %>

New

<%= date_input(f, :date_start, class: "form-control", id: "inlineFormInputGroupDatestart", placeholder: "Data") %>

About the filter_ecdr issue
I ended up taking another path.
I added within the index, in the controller
date_start = get_in(params, ["date_start"])

Thank you all for help.

2 Likes