Challenge
To build drop down menu like angular-ui-bootstrap with angularjs using "component":
Behaviors
- Menu toggle button Should open hidden menu when clicked, and to hide the menu if it open
- Menu Should Close itself when users clicks on some other place
- 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