2/22/17

Building Drop Down Menu Using Angular1 Component

Challenge

To build drop down menu like angular-ui-bootstrap with angularjs using "component":

Behaviors

  1. Menu toggle button Should open hidden menu when clicked, and to hide the menu if it open
  2. Menu Should Close itself when users clicks on some other place
  3. If disabled - should not show anything when clicked

Step 1 - CSS & HTML

For sake of simplicity i will copied styles from bootstrap framework:


.open > .dropdown-menu {
    display: block;
}
.dropdown-menu {
    position: absolute;
    top: 100%;
    left: 0;
    z-index: 1000;
    display: none;
    float: left;
    min-width: 160px;
    padding: 5px 0;
    margin: 2px 0 0;
    font-size: 14px;
    text-align: left;
    list-style: none;
    background-color: #fff;
    -webkit-background-clip: padding-box;
    background-clip: padding-box;
    border: 1px solid #ccc;
    border: 1px solid rgba(0, 0, 0, .15);
    border-radius: 4px;
    -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, .175);
    box-shadow: 0 6px 12px rgba(0, 0, 0, .175);
}

and the html will be following:

    <dropdown>
        <dropdown-toggle>
        <a href="">Dropdown <span class="caret"></span>
        </a>
        </dropdown-toggle>
        <dropdown-menu>
        <li>
            <a href="" ng-click="$ctrl.sayHi()">Action</a>
        </li>
        <li>
            <a href="">Another action</a>
        </li>
        <li>
            <a href="">Something else here</a>
        </li>
        <li role="separator" class="divider"></li>
        <li>
            <a href="">Separated link</a>
        </li>
        </dropdown-menu>
    </dropdown>

inside 'dropdown-toggle' container user should be able to place html of 'a' tag or button (according to his needs)
inside 'dropdown-menu' container user should be able to place html of menu items - there he should be able to attach methods from controller - like sayHi

dividing to components

I decided to divide this component to three different components:
The dropdown Parent component and dropdownToggle and dropdownMenu child components
That way logic will be splitted to its specific component

dropdown component

Should contain close and open methods


angular.module('drop',[])
.component('dropdown',{ 
  controller:function($element,$document) {
    $element.addClass('dropdown');//
    this.close=() => {
      $element.removeClass('open');
      this.isOpen = false;
    }
    this.open=() => {
      $element.addClass('open');
      this.isOpen = true;
    }    
  }
})

The mechanic here is that menu is visible when the main container (dropdown element) have "dropdown" and "open" classes, that why close and open are adding and removing 'open' classes accordingly

dropdownToggle component

Is the element that must handle the clicks and activate its parent 'open' and 'close' methods;
For be able to access its parent methods i'm using require option inside component definition:


.component('dropdownToggle',{
  require: {//<--USING REQUIRE OPTION
    parent: '^^dropdown' 
  },
  controller:function($element) {
    $element.addClass('dropdown-toggle');
    
    $element.on('click',($event)=>{
       this.parent.isOpen ? this.parent.close() : this.parent.open();
    })     
  }
})

click outside behavior

For be able to close the menu when user clicks somewhere else on the screen i'm taking advantage of $document service:
dropdown.js



 ...
  $document.on('click',(evt)=>{
    if(!$element.find('dropdown-toggle')[0].contains(evt.target)){
      this.close() 
    }
  })
...

You can see running code on this punker

Getting started with docker

It is very simple to get started usig docker. All you need to do-is download the docker desktop for your system Once you get docker syste...