User:Admin

From ElixirBlocks
Jump to: navigation, search

https://medium.com/@fraiha26/pushing-events-to-livecomponents-from-js-hooks-w-phoenix-elixir-d5544d4c3dfa

Changeset notes and forms

NOTE: Use case statements for pattern matching ____________________________________________

Create a basic <.form></form> without error handling

<.form :let={f} for={@changeset}  action={~p"/items"} >

  <.input field={f[:title]} type="text" label="Title" />
  
  <.button>Save Item</.button>
   
</.form>

With error handling looks like this:




<.form :let={f} for={@changeset}  action={~p"/items"} >

  <.error :if={@changeset.action}>
    Oops, something went wrong! Please check the errors below.
  </.error>


  <.input field={f[:title]} type="text" label="Title" />
  
  <.button>Save Item</.button>
   
</.form>

Create the routes for render and post to endpoint.


The controller for the page render needs to pass in the changeset via ```Items.change_item(%Item{})```


  def new(conn, _params) do
    changeset = Items.change_item(%Item{})
    render(conn, :new, changeset: changeset)
  end


Post route without error handling


  def create(conn, %{"item" => item_params}) do
    case Items.create_item(item_params) do
      {:ok, item} ->
        conn
        |> put_flash(:info, "Item created successfully.")
        |> redirect(to: ~p"/items/#{item}")
    end
  end

With error handling

  def create(conn, %{"item" => item_params}) do
    case Items.create_item(item_params) do
      {:ok, item} ->
        conn
        |> put_flash(:info, "Item created successfully.")
        |> redirect(to: ~p"/items/#{item}")

      {:error, %Ecto.Changeset{} = changeset} ->
        render(conn, :new, changeset: changeset)
    end
  end


_____________________________________________ https://medium.com/@vincentlin/phoenix-very-simple-form-c4b121697fcb


home.html.heex

<form action="/submit" method="post">
  <label for="name">Name:</label><br>
  <input type="text" id="name" name="name"><br>
  <input type="hidden" name="_csrf_token" value={@csrf_token} />
  <input type="submit" value="Submit">
</form>


page_controller.ex



defmodule AppWeb.PageController do
  use AppWeb, :controller

  def home(conn, _params) do
    # The home page is often custom made,

    csrf_token = Plug.CSRFProtection.get_csrf_token()
    render(conn, :home, csrf_token: csrf_token)
  end


  def submit(conn, _params) do
    # The home page is often custom made,
     IO.inspect(_params)
     IO.inspect "___________________________"
    csrf_token = Plug.CSRFProtection.get_csrf_token()
    render(conn, :home, csrf_token: csrf_token)
  end

end


router.ex

  scope "/", AppWeb do
    pipe_through :browser
    post "/submit", PageController, :submit
    get "/", PageController, :home

  end









Ecto.Adapters.SQL.query(Repo, "select * from artists where id=1")


Ecto cheatsheet: https://hexdocs.pm/ecto/crud.html __________________________________________________________________ Most programming languages give you the ability to set global state by creating globally scoped variables. Elixir does not do this. Instead, Elixir provides a tool called a "processes" to persist state. Elixir applications are composed of these "processes".

Processes by themselves are complicated to use, thus Elixir provides abstractions to make working with processes easier. One of these abstractions is called a GenServer.

GenServers are not only used to store state, they are also used to invoke work independent of the main application process.

Creating a GenServer to Store State

To Create a GenServer, start by creating a basic Module that uses the GenServer module (image below)


defmodule App do  
  use GenServer


end

We now need to describe the actions we want to perform. Being that this is used to store state, we will use basic CRUD actions: Create, Read, Update, Delete. To begins, empty functions describing each action is created.


defmodule App do
   def create do 

   end
   def read do 

   end

   def update do  

   end 

   def delete do 

   end

end

What now?

To fill in the functions with the appropriate code we must first have a basic understanding of how GenServers are structured.

GenServers are composed of the GenServer module invoking callbacks. The callbacks have built in names that you, as a developer have to use.

These callbacks are named:

  • init
  • handle_cast
  • handle_call


To demonstrate, here is an example :


{:ok, pid} = GenServer.start(ModuleNameOfGenServer, %{})

The above function named "start" calls the function named init in the Module below. In GenServer syntax the name of the GenServer functions always invoke callbacks that are determined by the language.


defmodule ModuleNameOfGenServer do
  # 1
  def init(state \\ %{}) do
    {:ok, state}
  end


This is a list of the GenServer functions and their corresponding callbacks.

  • GenServer.start/1 --> init/1
  • GenServer.call/3 --> def handle_call/3
  • GenServer.cast/2 --> handle_cast/2


Start and Call

Call and Cast can be used to perform the same operations. The

__________________________________________________________ Genserver timed events

defmodule Go do
  use GenServer

  def go() do
    GenServer.start_link(__MODULE__, [])
  end

  def init(list) do
    :timer.apply_interval(1000, __MODULE__, :add, [self(), "weeeee"])
    {:ok, list}
  end

  def view(pid) do
    GenServer.call(pid, :view)
  end

  def handle_call(:view, _from, list) do
    {:reply, list, list}
  end

  def add(pid, new_item) do
    GenServer.call(pid, {:item, new_item})
  end

  def handle_call({:item, new_item}, _from, state) do
    result = [new_item] ++ state
    {:reply, result, result}
  end
end


defmodule ShoppingList do
  use GenServer

  def start_link() do
    GenServer.start_link(ShoppingList, [])
  end

  def init(list) do
    {:ok, list}
  end

  def view(pid) do
    GenServer.call(pid, :view)
  end

  def handle_call(:view, _from, list) do
    {:reply, list, list}
  end

  def add(pid, new_item) do
    GenServer.call(pid, {:item, new_item})
  end

  def handle_call({:item, new_item}, _from, state) do
    result = [new_item] ++ state
    {:reply, result, result}
  end
end


https://blog.appsignal.com/2018/06/12/elixir-alchemy-deconstructing-genservers.html


defmodule KeyValue do
  # 1
  def init(state \\ %{}) do
    {:ok, state}
  end
 
  # 2
  def handle_cast({:put, key, value}, state) do
    {:noreply, Map.put(state, key, value)}
  end
 
  # 3
  def handle_call({:get, key}, _from, state) do
    {:reply, Map.fetch!(state, key), state}
  end
end

# {:ok, pid} = GenServer.start(KeyValue, %{})
# GenServer.cast(pid, {:put, :foo, "bar"})
# GenServer.call(pid, {:get, :foo})




Send messages via these functions wrapped in module functions .You can look at these are functions used to fire "GenServer Events".

  • start_link
  • GenServer.call
  • GenServer.cast

The corresponding server functions that reply are:

  • init
  • handle__call
  • handle_cast

These can be looked at as "listeners" - in JavaScript parlance. __________________________________________

If you have experience with programming languages other than Elixir the idea of GenServers may be very foreign and require a strong context shift to learn. This document is intended to simplify the concept.

  • What: GenServers are simply a way to hold state and/or invoke actions based on incoming events.
  • when
  • where
  • why
  • how





___________________________________________ GenServer timer example

defmodule Timer do
  use GenServer

  def init(_) do
    :timer.send_interval(1000, :xyz)
    {:ok, 0}
  end

  def handle_info(:xyz, state) do
    IO.inspect(state)
    {:noreply, state + 1}
  end
end


[
   {
     data:{
        creationTime,
        publishDate,
     }

     featured:{
        setAmount,
        items:[]
     },

     children:{
        setAmount,
        items:[]
     },
     links:{
        setAmount,
        items:[]
     }

   }

]

jason.encode / jason.decode


  def run(num) do

    Enum.each(0..99999, fn(_x) ->
      task = Task.async(fn -> num + _x end)
    IO.inspect Task.await(task)
  end)

Enum.map(feed.entries, fn x -> %{url: x.url, title: x.title} end)

App is split:

App that captures RSS in UI and lets admin select stories. THere are settings to determine the number of FEATURED,CHILD and LINK stories. News categories are horizontal and scrollable. This exports a json doc with all RSS data for each story. The



App that reads the json data. This app creates tables and is the front end.



https://stephenbussey.com/tags/elixir.html

defmodule AppWeb.GroupLive do
  use AppWeb, :live_view
  alias App.TestBeds
  alias App.Groups

  def mount(_params, _session, socket) do
    {:ok, assign(socket, testbeds: TestBeds.list_testbeds(), groups: Groups.list_groups())}
  end

  def handle_event("select-group", params, socket)  do  
     group = App.Groups.get_group!(params["group-id"])
     testbed = App.TestBeds.get_test_bed!(params["testbed_id"])
     App.TestBeds.update_test_bed(testbed, %{group_id: group.id})
     {:noreply, socket}
  end

  def updated_groups(testbed_id)  do  
     testbed = App.TestBeds.get_test_bed!(testbed_id)
     groups = App.Groups.list_groups
     tail = Enum.filter(groups, fn(item)-> item.id !== testbed.group_id end)
     head = Enum.filter(groups, fn(item)-> item.id == testbed.group_id end)
            |>Enum.at(0)
    [head | tail]
  end

  def render(assigns) do
    ~H"""
        <%= for testbed <- Enum.sort_by(@testbeds , &("#{&1.name}"), :asc)do %>
        <div class="grid grid-cols-4 gap-4 p-8 rounded shadow bg-white">
          <h2> Name</h2>
          <div class="name"><%= testbed.name %> </div>
            <h2> Group</h2>
          <div>
            <form phx-change="select-group" name="testbed_id"  >
               <input type="hidden" name="testbed_id" value={testbed.id}/>
               <select id="group-selection"  name="group-id" class="">
                <%= for group <- updated_groups(testbed.id) do %>
                  <%= if group !== nil do %>
                      <option value= {"#{group.id}"}><%=group.name %></option>
                      <%else%>
                      <option value= {"Unassigned"}>NONE</option>
                  <% end %>

                <% end %>
               </select>
            </form>
          </div>
        </div>
       <% end %>
    """
  end
end


Developer Notes

Groups Feature

To complete this feature do these things.

  • Run through the steps in | this document to create Group table.
  • Groups have these fields name: string, description: string and color: string
  • Add unique constraint in migration file:
  def change do
     create unique_index(:groups, [:name])
  end
  • Port over Group LiveView code (temporarily placed at discussion page).
  • On completion possibly, create a Group named No Group and set all Testbeds to it.



________ https://blog.appsignal.com/2022/10/11/phoenix-liveview-018-new-special-html-attributes.html https://blixtdev.com/whats-new-phoenix-1-7/ https://github.com/devato/inertia_phoenix https://phoenixonrails.com/blog/you-can-stop-using-form-for-in-phoenix


https://samuelmullen.com/articles/phoenix_templates_rendering_and_layouts

WorkFlow of Todo App in Both LiveView and DeadView.

Note:

Create foreign key data. Create live view. List all testbeds and form to assign each a group by group-name.

Repeat in dead view


Mental model and data flows


home.html.heex <a href="/data" name="we are the world" > click me</a>

<%= @message %>


page controller

defmodule AppWeb.PageController do
  use AppWeb, :controller

  def home(conn, _params) do
    IO.inspect _params
    render(conn, :home, message: "Hello")
  end

end

get "/:yay", PageController, :index

get "/", PageController, :index

  • Explain control flow of "Dead Views" and the Generator HTML Control flow.
  • Proper Way to use Modal
  • How to give Focus to Fields in Modal (or other forms fields)
  • How to Create Database Seed Data.
  • How to structure code so controller code is in one file and HTML Heex is in another
  • How to Use Generators in a Real World Project
  • How to Work with Database Data via Basic Commands and Custom Change sets.
  • How to Render Database Data to LiveView (via Context Commands).
  • How to Create a Chat using PubSub.
  • How to Create Event Handlers in LiveView
  • How to Create a Dynamic Route from Scratch.
  • How to Use Elixirs Template language
  • How to Work with Modal
  • How to Use CSS From Scratch (Without bundler)
  • How to use JS hooks


______________________ https://studioindie.co/blog/heex-guide/

get "/", PageController, :some-atom    #This is the route. some-atom is the controller name


defmodule AppWeb.PageController do
  use AppWeb, :controller

  def some-atom(conn, _params) do                 # controller name
    # The home page is often custom made,
    # so skip the default app layout.
    render(conn, :some-atom, layout: false)      # :some-atom is template name
  end
end

Controllers page_html.ex contains

defmodule AppWeb.PageHTML do
  use AppWeb, :html

  embed_templates "page_html/*"
end

page_html is the name of the folder in Controllers/page_html


Controllers/page_html/some-atom.html.heex (this is the template)



__________________________________________ Set all association with preload:

  def list_groups do
    Repo.all(Group)
     |> Repo.preload([:testbeds])
  end

Insert Child into Parent

Example:

App.get_group!(2) # arg is id
|> Ecto.build_assoc(:testbeds)
|> Repo.insert()

Or

group = App.get_group!(2) # arg is id

testbeds = Ecto.build_assoc(group, :testbeds)

App.Group.create_group(testbeds)
group = App.Groups.get_group!(1)
thing = Ecto.build_assoc(group, :testbeds, name: "DUMB")
alias App.{Repo}
Repo.insert(thing)


Example

avatar = %Avatar{nick_name: "Elixir", pic_url: "http://elixir-lang.org/images/logo.png"}
user = %User{name: "John Doe", email: "john.doe@example.com", avatar: avatar}

https://www.reddit.com/r/elixir/comments/oh318k/is_there_a_way_to_avoid_ectoassociationnotloaded/h4mmcfi/?utm_source=share&utm_medium=web3x&utm_name=web3xcss&utm_term=1&utm_content=share_button

  1. Create Group Table
mix phx.gen.html Groups Group groups name:string

Set the resources /

mix ecto.migrate


For testbeds create a migration file and add this field: group_id:references:groups

def change do
    alter table(:testbeds) do    # customize to your code
      add :group_id, references(:groups)
    end
  end


Update Groups Schema (Don't forget alias / has_many)


defmodule App.Groups.Group do
	use Ecto.Schema
	import Ecto.Changeset
	alias App.Testbeds.Testbed   # Alias!

	schema "posts" do
		field :body, :string
		field :title, :string
		has_many :testbeds, Testbed    # Has many

		timestamps()
	end

	@doc false
	def changeset(post, attrs) do
		post
		|> cast(attrs, [:title, :body])
		|> validate_required([:title, :body])
	end
end

TESTBEDS Schema

defmodule App.Testbeds.Testbed do
  use Ecto.Schema
  import Ecto.Changeset
  alias App.Groups.Group      # Alias
  schema "testbeds" do
    field :name, :string
    belongs_to :group, Group    # Belongs to
    timestamps()
  end

  @doc false
  def changeset(testbed, attrs) do
    testbed
    |> cast(attrs, [:name])
    |> validate_required([:name])
  end
end

_____________________________________ Side notes.

defmodule App.Repo.Migrations.CreateTestbeds do

 use Ecto.Migration
 def change do
   create table(:testbeds) do
     add :name, :string
     add :group_id, references(:groups, on_delete: :nothing)
     timestamps()
   end
   create index(:testbeds, [:group_id])
 end

end


________________________________

mix phx.gen.context Comments Comment comments name:string content:text post_id:references:posts


mix phx.gen.html Testbeds Testbed testbeds name:string group_id:references:groups

https://graffino.com/web-development/first-steps-in-elixir-and-phoenix-create-a-blog-prototype


https://blog.logrocket.com/getting-started-ecto-phoenix/

https://serokell.io/blog/ecto-guide-for-beginners


mix phx.gen.html Testbeds Testbed testbeds name:string

mix phx.gen.html Groups Group groups name:string

Create migration for


mix ecto.gen.migration add_testbed_group_reference


Migration:

  def change do
    alter table(:testbeds) do    # customize to your code
      add :group_id, references(:groups)
    end
  end


Manually Change:


defmodule App.Group do
  use Ecto.Schema

  schema "groups" do
    field :title, :string
    field :tagline, :string
    has_many :testbeds, App.Testbed
  end
end

testbeds

defmodule App.Repo.Migrations.AddTextbedGroups do
  use Ecto.Migration

  def change do
    create table(:testbeds) do
      add :name, :string
      add :group_id, references(:groups)
    end
  end
end


# lib/app/testbeds.ex

defmodule App.Testbed do
  use Ecto.Schema

  schema "testbedss" do
    field :name, :string
    belongs_to :group, App.Group
  end
end






_____________________

%App.TestBeds.TestBed{

 __meta__: #Ecto.Schema.Metadata<:loaded, "testbeds">,
 id: 1,
 developer: "None",
 name: "sdfsd",
 owner: "sdfsddf",
 note: "sdsfsddf",
 status: "Available",
 url: "sdf",
 version: "ddf",
 manager: "sf",
 inserted_at: ~N[2023-08-21 13:45:39],
 updated_at: ~N[2023-08-21 13:45:44]

} ________________________________________________________________---

defmodule AppWeb.SendLive do
  use AppWeb, :live_view
  
  def mount(_params, _session, socket) do
    {:ok, socket}
  end
  
  
  def handle_event("send", %{"text" => text}, socket) do

    AppWeb.Endpoint.broadcast(topic, "message", text)
    {:noreply, socket}
  end
  

  defp topic do
    "chat"
  end


  def render(assigns)do   
   ~H"""
<div>
  <h1>Send Message</h1>

  <form phx-submit="send">
    <input type="text" name="text" />
    <button type="submit">Send</button>
  </form>
</div>

"""
  end
  
end

______________________________________________________________


defmodule AppWeb.ReceiveLive do
  use AppWeb, :live_view
  
  def mount(_params, _session, socket) do
    if connected?(socket) do
      AppWeb.Endpoint.subscribe(topic)
    end
    {:ok, assign(socket, messages: "")}
  end
  
  def handle_info(%{event: "message", payload: message}, socket) do
    IO.inspect message
    {:noreply, assign(socket, messages: message)}
  end
  

  defp topic do
    "chat"
  end

  def render(assigns)do   
   ~H"""
<div>
  <h1>ChatLive</h1>
  <%= @messages %>


</div>

"""
  end
  
end

______________________________________________________________ When you hover over a list of items this hook finds items with the same name and sets the opacity of all others to 0. You see all the items similar to the one you are hovering over.


TestbedVersionHover: {
			// https://jsfiddle.net/IrvinDominin/7K2Z3/
            mounted() {

                let rows = document.getElementsByClassName("testbed-version")
                // this.ele = element
				// rows = this.ele;
                
				for (var i = 0; i < rows.length; i++) {
					rows[i].onmouseenter = function (event) {
						for (var j = 0; j < rows.length; j++) {
							let preversion = this.textContent;
							let version = preversion.slice(0,5)
							let compareVersion = rows[j].textContent.slice(0,5)
							if (compareVersion === version) continue
							rows[j].className += " other";
						}
					};
				
					rows[i].onmouseleave = function (event) {
						var hovers = document.getElementsByClassName('other');
						var len = hovers.length;
						for (var j = 0; j < len; j++) {
							hovers[0].className = hovers[0].className.replace(/\sother(\s|$)/, '');
						}
					};
				}

                // this.ele.addEventListener("click", (e) => {
                //     console.log(e.target)
                    
                // });

            }
        }


  • How to Create Database Seed Data.
  • How to structure code so controller code is in one file and HTML Heex is in another
  • How to Use Generators in a Real World Project
  • How to Work with Database Data via Basic Commands and Custom Change sets.
  • How to Render Database Data to LiveView (via Context Commands).
  • How to Create a Chat using PubSub.
  • How to Create Event Handlers in LiveView
  • How to Create a Dynamic Route from Scratch.
  • How to Use Elixirs Template language
  • How to Work with Modal
  • How to Use CSS From Scratch (Without bundler)
  • How to use JS hooks
  • Dissect Table Component and Update it to Order List in Reverse.

Selenium https://www.builder.io/blog/debug-nodejs

https://marketplace.visualstudio.com/items?itemName=ms-vscode.live-server#running-the-extension

This was added recently (see microsoft/vscode#109276).

   1. Open the palette (Ctrl + Shift + P)
   2. Select "Simple Browser: Preview"
   3. Enter web address

_______________________


const {By, Key, Builder} = require("selenium-webdriver");
const assert = require("assert")

require("chromedriver");

async function test_case(){
  let driver = await new Builder().forBrowser("chrome").build();
  await driver.get("sie");


  await driver.findElement(By.name('password')).clear();
  await driver.findElement(By.name('identification')).clear();
  await driver.findElement(By.name('identification')).sendKeys("bill");
  await driver.findElement(By.name('password')).sendKeys("password", Key.RETURN);




 setTimeout(()=>{
   driver.findElement(By.className("req_lan_license")).click();
 },5000)



defmodule AppWeb.PageLive do 

  use AppWeb, :live_view  
  alias App.Testbeds 
  def mount(_params, _session, socket)do   
     {:ok, assign(socket, testbeds: Testbeds.list_testbeds())}   
  end

    def button_event("invoke_editing") do
    %JS{}
    |> JS.remove_class("active", to: "#editing_button")
    |> JS.add_class("hidden", to: "#editing_button")
    |> JS.remove_class("hidden", to: "#preview_button")
    |> JS.add_class("active", to: "#preview_button")
    |> JS.remove_class("active", to: "#preview")
    |> JS.add_class("hidden", to: "#preview")
    |> JS.remove_class("hidden", to: "#editing")
    |> JS.add_class("active", to: "#editing")
  end

  def button_event("invoke_preview") do
    %JS{}
    |> JS.remove_class("active", to: "#preview_button")
    |> JS.add_class("hidden", to: "#preview_button")
    |> JS.remove_class("hidden", to: "#editing_button")
    |> JS.add_class("active", to: "#editing_button")
    |> JS.remove_class("active", to: "#editing")
    |> JS.add_class("hidden", to: "#editing")
    |> JS.remove_class("hidden", to: "#preview")
    |> JS.add_class("active", to: "#preview")

  end

  def handle_event("send", params, socket) do
    hide_modal("notes-modal-0")
    {:noreply, socket}
  end

  

  def render(assigns) do  
     ~H"""

    <%= for testbed <- @testbeds do %>
		<.modal id={"notes-modal-#{testbed.id}"}>
		    <.button phx-click={button_event("invoke_editing")} id="editing_button" class="hidden">
		      Preview
		    </.button>
		    <.button phx-click={button_event("invoke_preview")} id="preview_button">
		      Edit Me
		    </.button>
		    <div id="editing"> PREVIEWING THE WORK</div>
		    <form phx-submit = "send"   id="preview" class="hidden">
		      <textarea value = {testbed.note}></textarea>
		      <.button type="submit" phx-click={hide_modal("notes-modal-#{testbed.id}")}>SAVE WORK</.button>
		    </form>
		</.modal>

    <%= if testbed.note do %>
      <div phx-click={show_modal("notes-modal-#{testbed.id}")}>READ ME  ...<%= testbed.note %></div>
    <% end %>
	   
    <% end %>
       
      """
  end

end



import {HfInference} from "@huggingface/inference";
import dotenv from "dotenv";
dotenv.config()

const HF_ACCESS_TOKEN = process.env.HF_ACCESS_TOKEN

const hf = new HfInference(HF_ACCESS_TOKEN);


/*______Uncomment for this example______________________

const model = "nlpconnect/vit-gpt2-image-captioning";
const imageURL = "https://i.imgur.com/lTvb7Et.png";

const response = await fetch(imageURL);
const imageBlob = await response.blob();

const result  = await hf.imageToText({
	data: imageBlob,
	model: model,
});

_______________________________________________________*/



/*________Another example ___________________*/

const result = await hf.summarization({
	model: 'facebook/bart-large-cnn',
	inputs: "The role of a dumb man is to get smarter oogy bookie boo",
	parameters:{
		max_length: 100
	}
});





console.log(result);






________________________________________


https://fly.io/phoenix-files/sdeb-toggling-element/ https://www.youtube.com/watch?v=vBgZvQapqhs

https://blog.testdouble.com/posts/2022-11-28-how-to-use-javascript-with-phoenix-liveview/

https://hexdocs.pm/phoenix_live_view/0.19.3/Phoenix.Component.html _______________________________________ https://www.smashingmagazine.com/2011/10/quick-look-math-animations-javascript/

  • Depth first traversal
  • Breadth first traversal

https://dev.to/codesphere/10-algorithms-every-developer-should-learn-3lnm

https://www.youtube.com/watch?v=fPz40W9mfCg

__________________________________



defmodule AppWeb.GroupLive do
  use AppWeb, :live_view
  alias App.TestBeds
  alias App.Groups


  def mount(_params, _session, socket) do
    {:ok, assign(socket, testbeds: TestBeds.list_testbeds(), groups: Groups.list_groups())}
  end


  def handle_event("select-group", params, socket)  do  
         
       IO.inspect params
       {:noreply, socket}
  end


  def render(assigns) do
    ~H"""

  

  <div class="grid grid-columns-2 grid-flow-col gap-4">
      <%= for testbed <- @testbeds do %>
        <div class="column-1">
          <div class="name"><%= testbed.name %></div>
        </div>
        <div class="column-2">    
            <form phx-change="select-group"  >
               <input type="hidden" name="testbed_id" value={testbed.id}/>
               <select id="group-selection"  name="group-id" >
                <%= for group <- @groups do %>
                  <option value= {"#{group.id}"}  ><%=group.name %></option>
                <% end %>
               </select>
            </form>
         </div>
       <% end %>
   </div>

    """
  end
end


____________________________

defmodule AppWeb.GroupLive do
  use AppWeb, :live_view
  alias App.TestBeds
  alias App.Groups


  def mount(_params, _session, socket) do

    {:ok, assign(socket, testbeds: TestBeds.list_testbeds(), groups: Groups.list_groups(), messages: [])}
  end


  def handle_event("select-group", params, socket)  do  


      group = App.Groups.get_group!(params["group-id"])
       # IO.inspect group
       testbed = App.TestBeds.get_test_bed!(params["testbed_id"])
     
       App.TestBeds.update_test_bed(testbed, %{group_id: group.id})
         
    
       {:noreply, socket}
  end


  def updated_groups(testbed_id)  do  
       groups = App.Groups.list_groups
       tail = Enum.filter(groups, fn(item)-> item.id !== testbed_id end)
       head = Enum.filter(groups, fn(item)-> item.id == testbed_id end)
              |>Enum.at(0)
     [head | tail]
         
  end

 # def handle_event("groups", params, socket)do  
 #       selected_testbed = %{name: "my testbed name", id: 3}
 #       groups = App.Groups.list_groups
 #       tail = Enum.filter(groups, fn(item)-> item.id !== selected_testbed.id end)
 #       head = Enum.filter(groups, fn(item)-> item.id == selected_testbed.id end)
 #              |>Enum.at(0)
         
 
 #       # result = Enum.filter([1, 2, 3], fn x -> x.id !== 1 end)
 #      IO.inspect [head | tail]
         
 #      #
 #      {:noreply, socket, assigns.message:}
 #  end




  def render(assigns) do
    
    ~H"""
       <%!-- <div phx-click = "groups">CLICK ME</div> --%>
  


      <%= for testbed <- @testbeds do %>
        <div class="grid grid-cols-4 gap-4 p-8 rounded shadow bg-white">
          <h2> Name</h2>
          <div class="name"><%= testbed.name %> </div>
            <h2> Group</h2>
          <div>

            <form phx-change="select-group" name="testbed_id"  >
               <input type="hidden" name="testbed_id" value={testbed.id}/>
               <select id="group-selection"  name="group-id" class="">
        
                <%= for group <- updated_groups(testbed.id) do %>
                  <option value= {"#{group.id}"}  ><%=group.name %></option>
                  <!-- Write code to reorder with testbed.group_id matching group_id -->
                <% end %>
               </select>
            </form>
          </div>
        </div>
       <% end %>
   
    """
  end
end



_____________________________________


defmodule AppWeb.PageLive do
   use AppWeb, :live_view  
     alias App.Testbeds
  alias App.Groups
   def mount(_params, _session, socket)  do
	     {:ok, assign(socket, testbeds: Testbeds.list_testbeds(), groups: Groups.list_groups())}
  end

   def handle_event("select-group", params, socket)  do  
       group = App.Groups.get_group!(params["group-id"])
       testbed = App.Testbeds.get_testbed!(params["testbed_id"])
       result = App.Testbeds.update_testbed(testbed, %{group_id: group.id})
       IO.inspect result
      
       {:noreply, socket}
  end

    def updated_groups(testbed_id)  do  
       testbed = App.Testbeds.get_testbed!(testbed_id)
       groups = App.Groups.list_groups
       tail = Enum.filter(groups, fn(item)-> item.id !== testbed.group_id end)
       head = Enum.filter(groups, fn(item)-> item.id == testbed.group_id end)
              |>Enum.at(0)
 
       [head | tail]
    end


  def render(assigns) do
    
    ~H"""

      <%= for testbed <- @testbeds do %>
        <div class="grid grid-cols-4 gap-4 p-8 rounded shadow bg-white">
          <h2> Name</h2>
          <div class="name"><%= testbed.name %> </div>
            <h2> Group</h2>
          <div>

            <form phx-change="select-group" name="testbed_id"  >
               <input type="hidden" name="testbed_id" value={testbed.id}/>
               <select id="group-selection"  name="group-id" class="">
        
                <%= for group <- updated_groups(testbed.id) do %>

                  <%!-- <option value= {"#{group.id}"}><%=group.name %></option> --%>
                  <!-- Write code to reorder with testbed.group_id matching group_id -->

                  <%= if group !== nil do %>
                      <option value= {"#{group.id}"}><%=group.name %></option>
       
                  <% end %>


                <% end %>
               </select>
            </form>
          </div>
        </div>
       <% end %>
   
    """
  end
end
  has_many :testbeds, TestBed, on_delete: :restrict   # Has many

https://elixirforum.com/t/ecto-has-many-on-delete-protect/49550

https://hexdocs.pm/ecto/Ecto.Changeset.html#unique_constraint/3