<!DOCTYPE html>
<html>
<head>
<title>Angular 2 Dropdown - Example</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- CSS file -->
<link rel="stylesheet" type="text/css" href="style.css">
<!-- Latest compiled and minified CSS -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" >
<!-- IE polyfills, keep the order please -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/es6-shim/0.33.3/es6-shim.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/systemjs/0.19.20/system-polyfills.js"></script>
<!-- Agular 2 -->
<script src="https://code.angularjs.org/2.0.0-beta.7/angular2-polyfills.js"></script>
<script src="https://code.angularjs.org/tools/system.js"></script>
<script src="https://code.angularjs.org/tools/typescript.js"></script>
<script src="https://code.angularjs.org/2.0.0-beta.7/Rx.js"></script>
<script src="https://code.angularjs.org/2.0.0-beta.7/angular2.dev.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.12.0/moment.min.js"></script>
<!-- Config Agular 2 and Typescript -->
<script>
System.config({
transpiler: 'typescript',
typescriptOptions: { emitDecoratorMetadata: true },
packages: {'app': {defaultExtension: 'ts'}}
});
System.import('app/main')
.then(null, console.error.bind(console));
</script>
</head>
<!-- Run the application -->
<body>
<h1>Angular 2 Dropdown</h1>
<my-app class="container" style="display: block">Loading Sample...</my-app>
<div style="padding-top:50px">
<a target="_blank" href="http://www.angulartypescript.com/angular-2-tutorial/" title="Angular 2 Tutorial">
<img src="http://www.angulartypescript.com/wp-content/uploads/2016/03/learn-more-angular-2.png" alt="Smiley face" height="200" width="500">
</a>
<ul class="nav nav-pills nav-stacked" >
<li><a target="_blank" href="http://www.angulartypescript.com/angular-2-tutorial/" title="Angular 2 Home"> Angular 2 Tutorial </a></li>
<li><a target="_blank" href="http://www.angulartypescript.com/angular-2-introduction/">Angular 2 Introduction</a></li>
<li><a target="_blank" href="http://www.angulartypescript.com/angular-2-architecture/">Angular 2 Architecture</a></li>
<li><a target="_blank" href="http://www.angulartypescript.com/angular-2-annotations/">Angular 2 Annotations</a></li>
<li><a target="_blank" href="http://www.angulartypescript.com/angular-2-getting-started/">Angular 2 Setup</a></li>
<li><a target="_blank" href="http://www.angulartypescript.com/angular-2-hello-world/">Angular 2 Hello World</a></li>
<li><a target="_blank" href="http://www.angulartypescript.com/angular-2-components/">Angular 2 Components</a></li>
<li><a target="_blank" href="http://www.angulartypescript.com/angular-2-template-syntax/">Angular 2 Template Syntax</a></li>
<li><a target="_blank" href="http://www.angulartypescript.com/angular-2-data-binding/">Angular 2 Data Binding</a></li>
<li><a target="_blank" href="http://www.angulartypescript.com/angular-2-forms/">Angular 2 Forms</a></li>
<li><a target="_blank" href="http://www.angulartypescript.com/angular-2-formbuilder-example/">Angular 2 Formbuilder</a></li>
<li><a target="_blank" href="http://www.angulartypescript.com/angular-2-router-example/">Angular 2 Router</a></li>
<li><a target="_blank" href="http://www.angulartypescript.com/angular-2-http-example-typescript/">Angular 2 HTTP</a></li>
<li><a target="_blank" href="http://www.angulartypescript.com/angular-2-services/">Angular 2 Service</a></li>
</ul>
</div>
</body>
</html>
<!--
Copyright 2016 angulartypescript.com. All Rights Reserved.
Everyone can use this source code; don’t forget to indicate the source please:
http://www.angulartypescript.com/
-->
/* Styles go here */
/**
* Created by Tareq Boulakjar. from angulartypescript.com
*/
import {bootstrap} from 'angular2/platform/browser';
import {Angular2Dropdown} from './dropdown-example';
bootstrap(Angular2Dropdown);
/*
Copyright 2016 angulartypescript.com. All Rights Reserved.
Everyone can use this source code; don’t forget to indicate the source please:
http://www.angulartypescript.com/
*/
import {
Directive,
OnInit, OnDestroy, Input, Output, HostBinding,
EventEmitter, ElementRef, ContentChildren,
Query, QueryList
} from 'angular2/core';
import {dropdownService, NONINPUT} from './dropdown.service';
@Directive({selector: '[dropdown]'})
export class Dropdown implements OnInit, OnDestroy {
@HostBinding('class.open')
@Input() public get isOpen():boolean {
return this._isOpen;
}
@Input() public autoClose:string;
@Input() public keyboardNav:boolean;
@Input() public appendToBody:boolean;
@Output() public onToggle:EventEmitter<boolean> = new EventEmitter();
@Output() public isOpenChange:EventEmitter<boolean> = new EventEmitter();
@HostBinding('class.dropdown') private addClass = true;
private _isOpen:boolean;
// index of selected element
public selectedOption:number;
// drop menu html
public menuEl:ElementRef;
// drop down toggle element
public toggleEl:ElementRef;
constructor(public el:ElementRef,
@Query('dropdownMenu', {descendants: false}) dropdownMenuList:QueryList<ElementRef>) {
}
public set isOpen(value) {
this._isOpen = !!value;
if (this.appendToBody && this.menuEl) {
}
if (this.isOpen) {
this.focusToggleElement();
dropdownService.open(this);
} else {
dropdownService.close(this);
this.selectedOption = null;
}
this.onToggle.emit(this.isOpen);
this.isOpenChange.emit(this.isOpen);
}
ngOnInit() {
this.autoClose = this.autoClose || NONINPUT;
if (this.isOpen) {
}
}
ngOnDestroy() {
if (this.appendToBody && this.menuEl) {
this.menuEl.nativeElement.remove();
}
}
public set dropDownMenu(dropdownMenu:{el:ElementRef}) {
// init drop down menu
this.menuEl = dropdownMenu.el;
if (this.appendToBody) {
window.document.body.appendChild(this.menuEl.nativeElement);
}
}
public set dropDownToggle(dropdownToggle:{el:ElementRef}) {
// init toggle element
this.toggleEl = dropdownToggle.el;
}
public toggle(open?:boolean):boolean {
return this.isOpen = arguments.length ? !!open : !this.isOpen;
}
public focusDropdownEntry(keyCode:number) {
// If append to body is used.
let hostEl = this.menuEl ?
this.menuEl.nativeElement :
this.el.nativeElement.getElementsByTagName('ul')[0];
if (!hostEl) {
return;
}
let elems = hostEl.getElementsByTagName('a');
if (!elems || !elems.length) {
return;
}
switch (keyCode) {
case (40):
if (typeof this.selectedOption !== 'number') {
this.selectedOption = 0;
break;
}
if (this.selectedOption === elems.length - 1) {
break;
}
this.selectedOption++;
break;
case (38):
if (typeof this.selectedOption !== 'number') {
return;
}
if (this.selectedOption === 0) {
// todo: return?
break;
}
this.selectedOption--;
break;
}
elems[this.selectedOption].focus();
}
public focusToggleElement() {
if (this.toggleEl) {
this.toggleEl.nativeElement.focus();
}
}
}
import {ElementRef} from 'angular2/core';
export interface DropdownMenuInterface {
el: ElementRef;
templateUrl: string;
}
export interface DropdownToggleInterface {
el: ElementRef;
}
export const ALWAYS = 'always';
export const DISABLED = 'disabled';
export const OUTSIDECLICK = 'outsideClick';
export const NONINPUT = 'nonInput';
import {Dropdown} from './dropdown.directive';
export class DropdownService {
private openScope:Dropdown;
private dropdownScope:Dropdown;
private closeDropdownBind:EventListener = this.closeDropdown.bind(this);
private keybindFilterBind:EventListener = this.keybindFilter.bind(this);
public open(dropdownScope:Dropdown) {
if (!this.openScope) {
window.document.addEventListener('click', this.closeDropdownBind);
window.document.addEventListener('keydown', this.keybindFilterBind);
}
if (this.openScope && this.openScope !== this.dropdownScope) {
this.openScope.isOpen = false;
}
this.openScope = dropdownScope;
}
public close(dropdownScope:Dropdown) {
if (this.openScope !== dropdownScope) {
return;
}
this.openScope = null;
window.document.removeEventListener('click', this.closeDropdownBind);
window.document.removeEventListener('keydown', this.keybindFilterBind);
}
private closeDropdown(event:MouseEvent) {
if (!this.openScope) {
return;
}
if (event && this.openScope.autoClose === DISABLED) {
return;
}
if (event && this.openScope.toggleEl &&
this.openScope.toggleEl.nativeElement === event.target) {
return;
}
if (event && this.openScope.autoClose === NONINPUT &&
this.openScope.menuEl &&
/input|textarea/i.test((<any> event.target).tagName) &&
this.openScope.menuEl.nativeElement.contains(event.target)) {
return;
}
if (event && this.openScope.autoClose === OUTSIDECLICK &&
this.openScope.menuEl &&
this.openScope.menuEl.nativeElement.contains(event.target)) {
return;
}
this.openScope.isOpen = false;
}
private keybindFilter(event:KeyboardEvent) {
if (event.which === 27) {
this.openScope.focusToggleElement();
this.closeDropdown(null);
return;
}
if (this.openScope.keyboardNav && this.openScope.isOpen &&
(event.which === 38 || event.which === 40)) {
event.preventDefault();
event.stopPropagation();
this.openScope.focusDropdownEntry(event.which);
}
}
}
export let dropdownService = new DropdownService();
import {Directive, ElementRef} from 'angular2/core';
import {Dropdown} from './dropdown.directive';
@Directive({
selector: '[dropdown][dropdownKeyboardNav]',
host: {
'(keydown)': 'onKeydown($event)'
}
})
export class KeyboardNav {
constructor(private dd:Dropdown, private el:ElementRef) {
console.warn('keyboard-nav deprecated');
dd.keyboardNav = true;
}
onKeydown(event:KeyboardEvent) {
if (event.which !== 40 && event.which !== 38) {
return;
}
event.preventDefault();
event.stopPropagation();
let elems = this.dd.menuEl.nativeElement.getElementsByTagName('a');
switch (event.which) {
case (40):
if (typeof this.dd.selectedOption !== 'number') {
this.dd.selectedOption = 0;
break;
}
if (this.dd.selectedOption === elems.length - 1) {
break;
}
this.dd.selectedOption++;
break;
case (38):
if (typeof this.dd.selectedOption !== 'number') {
return;
}
if (this.dd.selectedOption === 0) {
// todo: return?
break;
}
this.dd.selectedOption--;
break;
}
elems[this.dd.selectedOption].nativeElement.focus();
}
}
import {
Directive, ElementRef, Host,
OnInit
} from 'angular2/core';
import {Dropdown} from './dropdown.directive';
@Directive({ selector: '[dropdownMenu]' })
export class DropdownMenu implements OnInit {
constructor( @Host() public dropdown: Dropdown, public el: ElementRef) {
}
public ngOnInit() {
this.dropdown.dropDownMenu = this;
}
}
import {
Directive, ElementRef, Host,
OnInit, Input, HostBinding, HostListener
} from 'angular2/core';
import {Dropdown} from './dropdown.directive';
@Directive({ selector: '[dropdownToggle]' })
export class DropdownToggle implements OnInit {
@HostBinding('class.disabled')
@Input() private disabled:boolean = false;
@HostBinding('class.dropdown-toggle')
@HostBinding('attr.aria-haspopup')
private addClass = true;
constructor(@Host() public dropdown:Dropdown, public el:ElementRef) {
}
public ngOnInit() {
this.dropdown.dropDownToggle = this;
}
@HostBinding('attr.aria-expanded')
public get isOpen() {
return this.dropdown.isOpen;
}
@HostListener('click', ['$event'])
public toggleDropdown(event:MouseEvent) {
event.stopPropagation();
if (!this.disabled) {
this.dropdown.toggle();
}
return false;
}
}
/**
* Created by Tareq Boulakjar. from angulartypescript.com
*/
import {Component} from 'angular2/core';
import {CORE_DIRECTIVES} from 'angular2/common';
import {Dropdown} from './dropdown.directive';
import {DropdownMenu} from './dropdown-menu.directive';
import {DropdownToggle} from './dropdown-toggle.directive';
/*Angular 2 Dropdown Menu*/
@Component({
selector: 'my-app',
template: `
<div (click)="$event.preventDefault()">
<!-- Angular 2 Simple Dropdown Menu -->
<h4>Angular 2 Simple Dropdown Menu</h4>
<span dropdown>
<a href id="angular-simple-dropdown" dropdownToggle>
angular 2 simple dropdown menu
</a>
<ul class="dropdown-menu" aria-labelledby="angular-simple-dropdown">
<li *ngFor="#item of dropDownItemsExample">
<a class="dropdown-item" href="#">{{item}}</a>
</li>
</ul>
</span>
<hr>
<!-- Angular 2 Dropdown Menu with Enable/Disable mode and Events (external click) -->
<h4>Angular 2 Dropdown Menu with Enable/Disable mode and Events (external click)</h4>
<div>
<button type="button" class="btn btn-warning btn-sm" (click)="dropdownMenu($event)">Dropdown Menu (Toggle)
</button>
<button type="button" class="btn btn-danger btn-sm" (click)="disabledMenu = !disabledMenu">Enable/Disable Dropdown Menu</button>
</div>
<div class="btn-group" dropdown [(isOpen)]="status.isopen">
<button id="dropdown-list" type="button" class="btn btn-default" dropdownToggle [disabled]="disabledMenu">
Button dropdown <span class="caret"></span>
</button>
<ul class="dropdown-menu" role="menu" aria-labelledby="dropdown-list">
<li role="menuitem"><a class="dropdown-item" href="#">Audi</a></li>
<li role="menuitem"><a class="dropdown-item" href="#">BMW</a></li>
<li role="menuitem"><a class="dropdown-item" href="#">Mercedes</a></li>
<li class="divider dropdown-divider"></li>
<li role="menuitem"><a class="dropdown-item" href="#">Maserati</a></li>
<li role="menuitem"><a class="dropdown-item" href="#">Porsche</a></li>
</ul>
</div>
<hr>
<!-- Angular 2 Dropdown Menu with Keyboard Accessibility -->
<h4>Angular 2 Dropdown Menu with Keyboard Accessibility</h4>
<div class="btn-group" dropdown keyboardNav="true">
<button id="dropdown-keyboard-access" type="button" class="btn btn-info" dropdownToggle>
Dropdown with Keyboard Accessibility (Up and Down) <span class="caret"></span>
</button>
<ul class="dropdown-menu" role="menu" aria-labelledby="dropdown-keyboard-access">
<li role="menuitem"><a class="dropdown-item" href="#">BMW Serie 1</a></li>
<li role="menuitem"><a class="dropdown-item" href="#">BMW Serie 5</a></li>
<li role="menuitem"><a class="dropdown-item" href="#">BMW Serie 3</a></li>
<li class="divider dropdown-divider"></li>
<li role="menuitem"><a class="dropdown-item" href="#">Porsche</a></li>
<li role="menuitem"><a class="dropdown-item" href="#">Audi New</a></li>
</ul>
</div>
</div>
`,
directives: [ Dropdown, DropdownMenu, DropdownToggle, CORE_DIRECTIVES],
})
export class Angular2Dropdown {
private disabledMenu:boolean = false;
private status:{isopen:boolean} = {isopen: false};
private dropDownItemsExample:Array<string> = ['BMW Serie 1', 'BMW Serie 2', 'BMW Serie 3', 'BMW Serie 4'];
private dropdownMenu($event:MouseEvent):void {
$event.preventDefault();
$event.stopPropagation();
this.status.isopen = !this.status.isopen;
}
}