bocaj

bocaj

How to validate a form with complex rules and nested data

I am trying to make a liveview form with complex validation. Below is an outline of what the form looks like on the page.

build a questionaire:
    custom questions:
        |text field| |delete button|
        |text field| |delete button|
        ...
        |"add" button|
    prebuilt questions:
        category 1:
            |checkbox for selecting question|, question text
            |checkbox for selecting question|, question text
            ...
        category 2:
            |checkbox for selecting question|, question text
            |checkbox for selecting question|, question text
            ...
        ...

There needs to be a limit on the total number of questions on the questionaire. There also needs to be a limit on the total number of questions in each group. For instance, a maximum of 3 custom questions, a maximum of 2 category_1 questions, etc.

I tried to implement a proof of concept form using inputs_for and a schema that looks like this:

defmodule QuestionaireBuilderSchema do
  use Ecto.Schema
  import Ecto.Changeset

  embedded_schema do
    embeds_one :questionaire, Questionaire do
      embeds_many :custom_questions, CustomQuestion do
        field :text, :string
      end

      embeds_many :category1_questions, Cat1Question, on_replace: :delete do
        field :text, :string
        field :selected, :boolean
      end

      embeds_many :category2_questions, Cat2Question, on_replace: :delete do
        field :text, :string
        field :selected, :boolean
      end
    end
  end

  def changeset(qbs, attrs) do
    qbs
    |> cast(attrs, [])
    |> cast_embed(:questionaire, with: &questionaire_changeset/2)
    |> custom_validation_max_total(:questionaire, max: 10)
  end

  def questionaire_changeset(questionaire, attrs) do
    questionaire
    |> cast(attrs, [])
    |> cast_embed(:custom_questions, with: &custom_question_changeset/2)
    |> cast_embed(:category1_questions, with: &cat_question_changeset/2)
    |> cast_embed(:category2_questions, with: &cat_question_changeset/2)
    |> custom_validate_num_selected(:custom_questions, max: 3, min: 0)
    |> custom_validate_num_selected(:category1, max: 2, min: 1)
  end

  def custom_question_changeset(question, attrs) do
    question
    |> cast(attrs, [:text])
    |> validate_required([:text])
  end

  def cat_question_changeset(question, attrs) do
    question
    |> cast(attrs, [:text, :selected])
  end
end

but one issue was the ability to add, edit, and remove the custom questions. The beginning of the article below describes the issue I had and suggests using individual forms for each question:

That worked great but I’m not sure how to cleanly validate the whole questionnaire since the individual questions now have their own forms. I should mention that, despite the title of the article I linked, this is not a question about how to solve this problem while also implementing liveview streams (although that would be an interesting question on its own).

To summarize, I feel like I could figure out how to make it work as a single form or as one form per question(potentially with liveview streams as well). But I wonder if anyone has any suggestions on what path they would take.

For reference I am on the latest versions of phoenix(1.7) and liveview(0.20).

Where Next?

Popular in Questions Top

chokchit
** (DBConnection.ConnectionError) connection not available and request was dropped from queue after 2733ms. You can configure how long re...
New
aadeshere1
I have a another noob question about loop. Since elixir is immutable, while loop is not directly possible. total = 10 while total != 0 ...
New
_russellb
I want to try my hand at web scraping. What tools/libraries do I need to use. I’m hoping to turn this into something professional so don’...
New
9mm
I am constructing a JSON object (map) and I need to conditionally set a field. I’m trying to write proper elixir-way code… and I’m at a l...
New
Darmani72
If I have a post route which an argument: post /my_post_route/:my_param1, MyController.my_post_handler How would get the post params ...
New
gshaw
What is the idiomatic way of matching for not nil in Elixir? E.g., First way: defp halt_if_not_signed_in(conn, signed_in_account) when...
New
mgjohns61585
Could someone help me? I’m making my first elixir program, number guessing game. I can’t figure out how to convert the user’s guess from ...
New
fireproofsocks
Forgive me if this is obvious, but how does one delete a database record WITHOUT selecting it first? Ecto.Repo — Ecto v3.14.0 has exampl...
New
fayddelight
I tried installing elixir 1.11.2 erlang 23.3.4 via asdf in my zsh shell. Enabled the versions locally and globally. When I list them ...
New
srinivasu
How to handle excepions in elixir? Suppose i have A, B, C ,D, E modules. and each module has get() function. A.get() method will call t...
New

Other popular topics Top

malloryerik
Hi, this is for people who, like me, have had some friction using .html.heex templates in VSCode. The solution seems to be, in a hyphena...
New
TunkShif
This post is an instruction guide to help you setup your Neovim for Elixir development from scratch. It includes general information on h...
274 42158 114
New
hariharasudhan94
lets say i have a sample like a = 20; b = 10; if (a > b) do {:ok, "a"} end if (a < b) do {:ok, b} end if (a == b) do {:ok, "equa...
New
New
AstonJ
Posting this to see if we can make things easier for people to get into Neovim. If you use Neovim and have a favourite distro please let ...
New
AstonJ
We’ve put together this wiki for Phoenix LiveView - please feel free to add any info you feel is worth including. What is Phoenix LiveV...
New
Nvim
Anybody knows a comprehensive comparison of Django and Phoenix, thanks for the help. Where are they similar? Where do they differ the m...
New
dblack
I’ve got an issue with an app and I’ve no idea of how to troubleshoot it. I’m hoping someone here might have seen something similar. I p...
New
saif
Hello everyone, Long time lurker first time poster here. I’ve recently begun working on Elixir full-time again! :raised_hands: It’s been...
New
hariharasudhan94
Lets say I have map like this fetching from my database %{"_id" => #BSON.ObjectId<58eb1a7a9ad169198c3dXXXX>, "email" => ...
New

We're in Beta

About us Mission Statement