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

zazaian
I recently generated the boilerplate for a new mix app using the Phoenix 1.3.1 phx.new generator and realized that the structure of the w...
New
zachallaun
Hey friends, wanted to share a tiny shell script I’ve been using to start Livebook with easy access to either a running production server...
New
tomciopp
TLS 1.3 has been out for a little over a year now, but it has been unavailable in Phoenix due to erlang’s handling of ssl. With the most ...
New
mhanberg
Hi! I recently finished adding authentication to my Phoenix API, so I wanted to share what I learned. I haven't created authentication ...
New
AstonJ
This was originally posted on my blog, but since my I’ve taken it down (I hadn’t posted anything on it for a while and since it was runni...
New
thetechnologyvault
One of our team members just published this getting started guide for Elixir/Phoenix devs to use the Nanobox platform: https://content.n...
New
Zurga
In a quest to optimize the amount of data sent between the server and client I recently decided to try to use MessagePack instead of JSON...
New
pinksynth
Whenever tests have to interact with an Ecto.Repo, sometimes it’s necessary to test a few different branches or paths that data can take....
New
ben-pr-p
Hey all! I put together a starter-pack / instructions to set up Phoenix with the new parcel-bundler instead of brunch (or webpack). It’s...
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

Other popular topics Top

JakeBecker
TL;DR: I’ve just released an implementation of Microsoft’s IDE-independent Language Server Protocol for Elixir. It adds language support ...
1140 51847 244
New
sorentwo
Hello! tl;dr Announcing Oban, an Ecto based job processing library with a focus on reliability and historical observability. After spen...
977 41022 311
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 th...
New
albydarned
Hello all! I am typing this post from my new MacBook Pro with the M1 chip. I’m loving it so far, and will probably use it as my daily dr...
New
romenigld
I am trying to run a deploy with docker and I successfully runned with this command: docker build -t romenigld/blog-prod . but when I t...
New
KronicDeth
Elixir plugin for JetBrain’s IntelliJ Platform (including Rubymine) This is a plugin that adds support for Elixir to JetBrains IntelliJ...
289 35421 110
New
polypush135
As many of you may have realized by now (sorry for all the posts here) I’ve been working on a db problem where I’m trying to aggregate a ...
New
sacepums
Hey guys. I'm new to elixir and im really stocked about it. But I ran into a bit of problem - I need to convert a date sting, for examp...
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
stefanluptak
Hello everybody, usually, I use a 29" ultra-wide monitor for VSCode which can easily accomodate explorer (files panel) + file with code ...
New

We're in Beta

About us Mission Statement