crockwave

crockwave

Integrate dropdown menus on app list elements

To integrate dropdown menus in a Phoenix Liveview app, you can use a combination of js, Hooks, CSS and your .leex and .ex code. You can expand on this concept to dynamically adjust your dropdown menu contents, and apply the dropdown in multiple places in your app.

.leex
Create adiv with one or more spans to define your dropdown menu. This example assumes you have multiple items to apply the menu to, you need to assign a unique value to the element, mapped against your app’s state. Apply as many span elements as you need for your menu options. Include a phx-hook reference if you want to listen for click and hover events to close the dropdown

You can customize your dropdown span rendering by examining socket.assigns values in the .leex file.

      <div class="element_container" id="element_container" phx-hook="DropdownContainer">
              <div class = "my_dropdown" phx-click="dropdown" onclick="dropdownOptions('<%= unique_element_id %>')">
                <div id="myDropDown<%= unique_element_id %>" class="dropdown-content">
                  <span phx-click="menu_option_1" phx-value-unique_element_id="<%= unique_element_id %>">Menu Option</span>
                </div>
              </div>
    </div>

Include a script element in your .leex to allow toggling of the dropdown menu on click events.

<script type="text/javascript">
        /* When the user clicks on the button, 
    toggle between hiding and showing the dropdown content */
      function dropdownOptions(player) {
        var b = "playerDropDown"
        var c = b.concat(player)
        document.getElementById(c).classList.toggle("show")
      }
</script>

CSS
Your CSS should include the following for dropdown rendering. The default behavior is display: none; until the show class is detected. The show class is added/removed/toggled by javascript in the .leex and .js files.

.chat_player_dropdown:hover, .chat_player_dropdown:focus {
  background-color: #ddd;
}

.my_dropdown {
  position: relative;
  font-weight: 100;
  font-size: 100%;
  color: #333333;
  cursor: pointer;
}

.dropdown-content {
  display: none;
  position: absolute;
  left: 24px;
  background-color: #f1f1f1;
  min-width: 55px;
  overflow: auto;
  box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
  padding-left: 10px;
  padding-right: 10px;
  padding-top: 5px;
  padding-bottom: 5px;
  z-index: 998;
}

.dropdown-content span {
  color: black;
  padding-left: 10px;
  padding-right: 10px;
  padding-top: 5px;
  padding-bottom: 5px;
  text-decoration: none;
  margin-top: 3px;
  line-height: 2.3;
}

.dropdown-content span:hover {
  background-color: #ddd;
}

.show {display: block;}

.js
You can add a Hook to allow the dropdown to disappear on click and hover events on nearby parts of the rendered DOM, to accommodate users that hover or click elsewhere after activating the dropdown

Hooks.DropdownContainer = {
	mounted() {

	    // Close the dropdown if the user clicks outside of it
	    window.onclick = function(event) {
		    if (!event.target.matches('.my_dropdown')) {
		      var dropdowns = document.getElementsByClassName('dropdown-content')
		      var i
		      for (i = 0; i < dropdowns.length; i++) {
		        var openDropdown = dropdowns[i]
		        if (openDropdown.classList.contains('show')) {
		          openDropdown.classList.remove('show')
		        }
		      }
		    }
	    }

		var item = document.getElementById("element_container");
	    item.addEventListener("mouseover", e => {
	       this.pushEvent("restore", {dropdown: "false"})
	    })

		var item = document.getElementById("other_element");
	    item.addEventListener("mouseover", e => {
	       this.pushEvent("restore", {dropdown: "false"})
	    })
	}
}

.ex
If you need to control how you render things while your dropdown is being displayed, you can track it by assigning a dropdown parameter in
socket.assigns, with a default value of “false”.

In some of your event handlers, you would assign dropdown to “false” as a means of allowing normal rendering to commence.

Add a handler as required to detect dropdown activation

  @impl true
  def handle_event("dropdown", _params, socket) do
    {:noreply, assign(socket, dropdown: "true")}
  end

Most Liked

crockwave

crockwave

You assign a unique_element_id to each element that you create with a for statement in your .leex file when you have a common element type, such as a list item. You would normally use a div to contain the unique identifier.

You can see this in action at https://shiptoaster.com, when you host a game, and click on a player or viewer in the left sidebar, where the list is a list of players/viewers, and each can have their own dropdown.

When you create your dropdown span elements in your .leex file, you can use your socket.assigns values to dynamically evaluate one or more of those values so that you can customize how your dropdown span elements are presented. As an example, if you mute a player, you can use that muted state to render a span’ that allows you to unmute that player.

Where Next?

Popular in Guides/Tuts Top

voltone
The EEF’s Security WG has released the first public draft of the Secure Coding and Deployment Hardening Guidelines for BEAM languages. “...
New
OndrejValenta
Me and my boys started a new website specifically designed for other ASP.NET programmers that struggle, as we do, with their transformati...
New
sergio
https://sergiotapia.me/generate-images-with-name-initials-using-elixir-and-imagemagick-374eca4d14ff Hope this saves you guys some time!...
New
dkuhlman
For those of you who might be interested in using ZeroMQ in Elixir, I converted the Erlang examples in the ZeroMQ “zguide” to Elixir. I ...
New
dogweather
I just finished a long process of configuring and debugging a Docker Compose-based dev environment. Several late-night hours! I think I’v...
New
f0rest8
Hi everyone, Just wanted to say that the new Self-referencing many to many guide is now up on the official Hex docs (at least I just not...
New
marcin
This post is intended to be a guide for others, I was running a remote debugger for the first time and appreciate feedback on how I could...
New
anuragg
We just published a guide to automatic clustering in Elixir 1.9, with Mix releases and libcluster. The cluster automatically discovers n...
New
AstonJ
With a few questions relating to this recently, I wonder if it might help having a thread where we share how we’ve set up Elixir/Erlang/P...
New
niku
I have published an elixir project with using Travis CI. I would like to share some tips &amp; thoughts that I was getting through this ...
New

Other popular topics Top

Brian
What is the proper way to load a module from a file in to IEX? In the python world, doing something like this pretty standard: from ....
New
peerreynders
Manning 2016 Halloween weekend sale via Deal of the Day Friday, October 28 - Half off all MEAPs - code WM102816LT Saturday, October 29 ...
326 29600 154
New
dotdotdotPaul
Okay, I'm having a heck of a time trying to figure out how to best handle the validation of belongs_to associations in Ecto. I'm sure I'...
New
yawaramin
In the Dialyzer docs ( http://erlang.org/doc/man/dialyzer.html#requesting-or-suppressing-warnings-in-source-files ), there is a way to tu...
New
ycv005
I have followed this StackOverflow post to install the specific version of Erlang. And When I am running mix ecto.setup then getting fol...
New
SoCreat
i’m a new one to elixir which editor can i use vs code? or atom? Thanks! :smiley:
New
jononomo
I am trying to figure out how Mix knows whether the environment is test, dev, or prod -- where is this set? Thanks.
New
msaraiva
Surface is an experimental library built on top of Phoenix LiveView and its new LiveComponent API that aims to provide a more declarative...
564 42633 214
New
AstonJ
by Lance Halvorsen Elixir and Phoenix are generating tremendous excitement as an unbeatable platform for building modern web application...
460 27162 124
New
siddhant3030
Hi, I have to write a raw query for one of my project. But till now I have used ecto queries and don’t have much experience writing raw ...
New

We're in Beta

About us Mission Statement