Need help with form fields

Create new project–>when user select a company and select it’s contact , after that if user change the company, it should shows blank contact filed for change company, and should not save the previous contact of company

I want to remove the selection from contacts and buildings when selecting other company contact. What will be the solution?
1234

form_component.html.heex


<div class="contacts_newcom_m">
    <h1><%= page_title(@action) %></h1>

   <div class="a_asside_account_c_first_ss mt-20">
      <.form let={f} for={@changeset} phx-target={@myself} phx-change="validate" phx-submit="save" phx-update="replace">
      <span>
        <%= if entry = List.first(@uploads.image.entries) do%>
          <%= live_img_preview entry %>
        <% else %>
          <%= if @project.project_image do%>
            <img src={@project.project_image}/>
          <% else %>
            <img src={Routes.static_path(@socket, System.get_env("DEFAULT_AVATAR"))} alt="img"/>
          <% end %>
        <% end %> 
        <span class="account_add_img" phx-drop-target={@uploads.image.ref} onclick={"document.getElementById('#{@uploads.image.ref}').click()"}>
            <img src={Routes.static_path(@socket, "/images/Frame 811261 (1).svg")} alt=""/>
            <%= gettext("add image") %>
            <%= live_file_input @uploads.image, hidden: true %>
            <%= if entry = List.first(@uploads.image.entries) do%>
              <%= for err <- upload_errors(@uploads.image, entry) do %>
                <span><%= image_errors(err) %></span>
              <% end %>
            <% end %>
        </span>
      </span>

         <div class="form-control">
            <p><%= gettext("name") %></p>
            <%= text_input f, :name %>
            <%= error_tag f, :name %>
         </div>
         <div class="form-control ">
            <p><%= gettext("description") %></p>
            <%= text_input f, :description %>
            <%= error_tag f, :description %>
         </div>
         
         <div class="form-control ">
            <p><%= gettext("Estimated Time") %></p>
            <%= text_input f, :estimated_time, type: "number", min: 0, oninput: "this.value = Math.abs(this.value)" %>
            <%= error_tag f, :estimated_time %>
         </div>

         <div class="form-control ">
            <p><%= gettext("Project Start Date") %></p>
            <%= datetime_local_input f, :project_start_date %>
            <%= error_tag f, :project_start_date %>
         </div>

         <div class="form-control ">
            <p><%= gettext("Company Contact") %></p>
            <%= select f, :company_contact_id, @company_contacts_options, options(:company_contact, assigns[:parent])  %>
            <%= error_tag f, :company_contact_id %>
         </div>

         <%= inputs_for f, :project_contacts, fn a -> %>
            <%= hidden_input a, :contact_id %>
            <%= hidden_input a, :company_id %>
         <% end %>

         <%= inputs_for f, :project_buildings, fn a -> %>
            <%= hidden_input a, :building_id %>
            <%= hidden_input a, :company_id %>
         <% end %>
               
         <%= hidden_input f, :stage, value: "pending" %>
         <%= error_tag f, :stage %>

         <%= hidden_input f, :lead_column_id %>
         <%= error_tag f, :lead_column_id %>
      
         <%= hidden_input f, :company_id, value: @current_user.company_id %>
         <%= error_tag f, :company_id %>

         <div class="form-control ">
            <p><%= gettext("Contacts") %></p>
            <%= live_component AppeoWeb.TagDropdown,
            id: :project_contacts,
            ex_tags: @ex_contact_taggings,
            module: %{name: __MODULE__, id: @id, field: :contact_id}, 
            tags: @contact_options,
            disabled: options(:contact, assigns[:parent])
            %>
            <%= error_tag f, :project_contacts %>
         </div>

         <div class="form-control">
            <p><%= gettext("Buildings") %></p>
            <%= live_component AppeoWeb.TagDropdown,
            id: :project_buildings,
            ex_tags: @ex_building_taggings,
            module: %{name: __MODULE__, id: @id, field: :building_id}, 
            tags: @building_options
            %>
             <%= error_tag f, :project_buildings %>
         </div>
         
         <div class="form-control ">
            <p><%= gettext("Project Type") %></p>
            <%= select f, :project_type, ["Roofing": "roofing", "Any Name": "any name"] %>
            <%= error_tag f, :project_type %>
         </div>

         <div class="form-control ">
            <button class="btn" type="submit"><%= gettext("save") %></button>
            <div class="btn"><%= live_patch gettext("Cancel"), to: @return_to %></div>
         </div>
      </.form>
   </div>
</div>

defmodule AppeoWeb.LeadsLive.FormComponent do
  use AppeoWeb, :live_component

  alias Appeo.Leads
  alias Appeo.Leads.Project
  alias Appeo.Contacts
  alias Ecto.Changeset
  alias Appeo.ProjectPermissions

  @bucket_dir_name "projects"

  @impl true
  def update(%{current_user: _, project: project} = assigns, socket) do
    params = default_params(project.id, assigns)
    changeset = Leads.change_project(project, params)

    company_contacts = Contacts.list_company_contacts_for_project()
    company_contacts_options = Enum.map(company_contacts, &{&1.name, &1.id})

    company_contact_id =
      project.company_contact_id ||
        company_contacts
        |> Enum.at(0, %{})
        |> Map.get(:id, "")

    company_contact_id = to_string(company_contact_id)

    building_options = building_options(company_contact_id, company_contacts)
    contact_options = contacts_options(company_contact_id, company_contacts)

    {ex_contact_taggings, ex_building_taggings} =
      if project.id do
        building_tags = Map.new(building_options, &{&1.id, &1})
        contact_tags = Map.new(contact_options, &{&1.id, &1})

        {
          Enum.map(project.project_contacts, &Map.get(contact_tags, &1.contact_id)),
          Enum.map(project.project_buildings, &Map.get(building_tags, &1.building_id))
        }
      else
        {[], []}
      end

    socket =
      socket
      |> assign(assigns)
      |> assign(:changeset, changeset)
      |> assign(:project, project)
      |> assign(:contact_options, contact_options)
      |> assign(:building_options, building_options)
      |> assign(:company_contacts_options, company_contacts_options)
      |> assign(:company_contacts, company_contacts)
      |> assign(:uploaded_files, [])
      |> assign(:ex_contact_taggings, ex_contact_taggings)
      |> assign(:ex_building_taggings, ex_building_taggings)
      |> allow_upload(:image,
        accept: ~w(.jpg .jpeg .png),
        max_entries: 1,
        max_file_size: 3_145_726
      )

    {:ok, socket}
  end

  def update(assigns, socket) do
    socket =
      case assigns do
        %{field: :contact_id, taggings: taggings} ->
          changeset = build_changeset(:contact_id, taggings, socket.assigns)
          assign(socket, changeset: changeset, ex_contact_taggings: taggings)

        %{field: :building_id, taggings: taggings} ->
          changeset = build_changeset(:building_id, taggings, socket.assigns)
          assign(socket, changeset: changeset, ex_building_taggings: taggings)
      end

    {:ok, socket}
  end

  @impl true
  def handle_event("validate", %{"project" => project}, socket) do
    changeset =
      socket.assigns.project
      |> Leads.change_project(project)
      |> Map.put(:action, :validate)

    company_contacts = socket.assigns.company_contacts
    contacts_options = contacts_options(project["company_contact_id"] || "", company_contacts)
    building_options = building_options(project["company_contact_id"] || "", company_contacts)

    socket =
      socket
      |> assign(:contact_options, contacts_options)
      |> assign(:building_options, building_options)

    {:noreply, assign(socket, :changeset, changeset)}
  end

  def handle_event("save", %{"project" => project_params}, socket) do
    socket
    |> upload_image(@bucket_dir_name)
    |> save_project(socket, socket.assigns.action, project_params)
  end

  def handle_event("project_contact", %{"index" => index}, socket) do
    changeset = socket.assigns.changeset
    project_contacts = remove_field(changeset.changes.project_contacts, index)

    changeset = put_in(changeset.changes.project_contacts, project_contacts)

    {:noreply, assign(socket, :changeset, changeset)}
  end

  def build_changeset(:contact_id, taggings, assigns) do
    company_id = assigns.current_user.company_id
    project_contacts = Enum.map(taggings, &%{company_id: company_id, contact_id: &1.id})

    assigns.changeset
    |> Changeset.put_assoc(:project_contacts, project_contacts)
    |> Changeset.apply_changes()
    |> Project.changeset()
    |> Map.put(:action, :validate)
  end

  def build_changeset(:building_id, taggings, assigns) do
    company_id = assigns.current_user.company_id
    project_buildings = Enum.map(taggings, &%{company_id: company_id, building_id: &1.id})

    assigns.changeset
    |> Changeset.put_assoc(:project_buildings, project_buildings)
    |> Changeset.apply_changes()
    |> Project.changeset()
    |> Map.put(:action, :validate)
  end

  defp save_project({:error, error}, socket, _, _) do
    {:noreply, put_flash(socket, :error, error)}
  end

  defp save_project({:ok, path}, socket, :edit_project, project_params) do
    path = path || socket.assigns.project.project_image
    project_params = Map.put(project_params, "project_image", path)

    case Leads.update_project(socket.assigns.project, project_params) do
      {:ok, _contact} ->
        {:noreply,
         socket
         |> put_flash(:info, gettext("Project updated successfully"))
         |> push_patch(to: socket.assigns.return_to)}

      {:error, %Ecto.Changeset{} = changeset} ->
        {:noreply, assign(socket, :changeset, changeset)}
    end
  end

  defp save_project({:ok, path}, socket, :new_project, project_params) do
    project_params =
      project_params
      |> Map.put("project_image", path)
      |> Map.put(
        "project_permission_joined_users",
        [
          %{
            "company_id" => socket.assigns.current_user.company_id,
            "user_id" => socket.assigns.current_user.id,
            "project_permission_id" => ProjectPermissions.get_permission_with_name("Owner").id
          }
        ]
      )

    case Leads.create_project(project_params) do
      {:ok, _project} ->
        {:noreply,
         socket
         |> put_flash(:info, gettext("Project created successfully"))
         |> push_redirect(to: socket.assigns.return_to)}

      {:error, %Ecto.Changeset{} = changeset} ->
        {:noreply, assign(socket, changeset: changeset)}
    end
  end

  defp default_params(nil, %{current_user: current_user} = assigns) do
    %{
      lead_column_id: assigns.lead_column_id,
      company_id: current_user.company_id
    }
  end

  defp default_params(_, _assigns), do: %{}

  defp contacts_options("", _assigns), do: []

  defp contacts_options(company_contact_id, company_contacts) do
    company_contacts
    |> Enum.find(&(to_string(&1.id) == company_contact_id))
    |> Map.get(:contacts)
    |> Enum.map(&%{name: &1.first_name, id: &1.id})
  end

  defp building_options("", _assigns), do: []

  defp building_options(company_contact_id, company_contacts) do
    company_contacts
    |> Enum.find(&(to_string(&1.id) == company_contact_id))
    |> Map.get(:buildings)
    |> Enum.map(&%{name: &1.building_name, id: &1.id})
  end

  defp remove_field(changeset_list, index) do
    changeset_list
    |> Enum.with_index()
    |> Enum.reduce([], fn {value, i}, acc -> if "#{i}" == index, do: acc, else: acc ++ [value] end)
  end

  defp page_title(:new_project), do: gettext("Create New Project")
  defp page_title(:edit_project), do: gettext("Update Project")

  defp options(:company_contact, [:contact, contact]),
    do: buid_options(contact.company_contact_id)

  defp options(:company_contact, [:company_contact, %{id: id}]), do: buid_options(id)
  defp options(:contact, [:contact, _]), do: true
  defp options(:contact, _), do: false
  defp options(_, _), do: []

  defp buid_options(id), do: [disabled: true, selected: id]
end

Anyone?

I think you have two options.

Option 1: In your validate event handler, LiveView will pass a _target field which you can use to check which field triggered the event. Here, you should be able to detect if it was the company_contact_id, and if so, update/reset your changeset to clear the dependent fields. Note that you may need some extra book-keeping to figure out if the selected company actually changed from its last value, as you probably don’t want to reset the other fields if the value didn’t change.

Option 2: I haven’t tried this myself yet, but LiveView 0.17.8 and up supports the phx-change event on individual inputs. So you should be able to add a specific company_changed event handler, where you can update your changeset and clear the other fields.

Hope this helps!