### File

`src/dropdown/dropdown.component.ts`

### Description

Drop-down lists enable users to select one or more items from a list.

See demo

../../iframe.html?id=dropdown--basic

### Implements

`OnInit` `AfterContentInit` `OnDestroy`

### Example

### Metadata

providers | `{ provide: NG_VALUE_ACCESSOR, useExisting: Dropdown, multi: true }`
selector | `ibm-dropdown`

template

    <label *ngIf="label" [for]="id" class="bx--label">
    	<ng-container *ngIf="!isTemplate(label)">{{label}}</ng-container>
    	<ng-template *ngIf="isTemplate(label)" [ngTemplateOutlet]="label"></ng-template>
    </label>
    <div *ngIf="helperText" class="bx--form__helper-text">
    	<ng-container *ngIf="!isTemplate(helperText)">{{helperText}}</ng-container>
    	<ng-template *ngIf="isTemplate(helperText)" [ngTemplateOutlet]="helperText"></ng-template>
    </div>
    <div
    	[id]="id"
    	class="bx--dropdown bx--list-box"
    	[ngClass]="{
    		'bx--dropdown--light': theme === 'light',
    		'bx--list-box--inline': inline,
    		'bx--skeleton': skeleton,
    		'bx--dropdown--disabled bx--list-box--disabled': disabled
    	}">
    	<button
    		type="button"
    		#dropdownButton
    		class="bx--list-box__field"
    		[ngClass]="{'a': !menuIsClosed}"
    		[attr.aria-expanded]="!menuIsClosed"
    		[attr.aria-disabled]="disabled"
    		(click)="toggleMenu()"
    		(blur)="onBlur()"
    		[disabled]="disabled">
    		<div
    			(click)="clearSelected()"
    			*ngIf="type === 'multi' && getSelectedCount() > 0"
    			class="bx--list-box__selection--multi"
    			title="Clear all selected items">
    			{{getSelectedCount()}}
    			<svg
    				focusable="false"
    				preserveAspectRatio="xMidYMid meet"
    				style="will-change: transform;"
    				role="img"
    				xmlns="http://www.w3.org/2000/svg"
    				width="16"
    				height="16"
    				viewBox="0 0 16 16"
    				aria-hidden="true">
    				<path d="M12 4.7l-.7-.7L8 7.3 4.7 4l-.7.7L7.3 8 4 11.3l.7.7L8 8.7l3.3 3.3.7-.7L8.7 8z"></path>
    			</svg>
    		</div>
    		<span *ngIf="isRenderString()" class="bx--list-box__label">{{getDisplayStringValue() | async}}</span>
    		<ng-template
    			*ngIf="!isRenderString()"
    			[ngTemplateOutletContext]="getRenderTemplateContext()"
    			[ngTemplateOutlet]="displayValue">
    		</ng-template>
    		<ibm-icon-chevron-down16
    			*ngIf="!skeleton"
    			class="bx--list-box__menu-icon"
    			[attr.aria-label]="menuButtonLabel"
    			[ngClass]="{'bx--list-box__menu-icon--open': !menuIsClosed }">
    		</ibm-icon-chevron-down16>
    	</button>
    	<div
    		#dropdownMenu
    		[ngClass]="{
    			'drop-up': dropUp
    		}">
    		<ng-content *ngIf="!menuIsClosed"></ng-content>
    	</div>
    </div>

### Index

###### Properties

- dropdownButton
- Static dropdownCount
- dropdownMenu
- dropUp
- keyboardNav
- menuIsClosed
- noop
- Protected onTouchedCallback
- outsideClick
- outsideKey
- propagateChange
- scroll
- view

###### Methods

- _appendToBody
- _appendToDropdown
- _keyboardNav
- _noop
- _outsideClick
- _outsideKey
- addScrollEventListener
- clearSelected
- closedDropdownNavigation
- closeMenu
- getDisplayStringValue
- getRenderTemplateContext
- getSelectedCount
- isRenderString
- Public isTemplate
- isVisibleInContainer
- ngAfterContentInit
- ngOnDestroy
- ngOnInit
- onBlur
- openMenu
- registerOnChange
- registerOnTouched
- removeScrollEventListener
- toggleMenu
- valueSelected
- writeValue

###### Inputs

- appendInline
- appendToBody
- disableArrowKeys
- disabled
- displayValue
- helperText
- id
- inline
- label
- menuButtonLabel
- placeholder
- scrollableContainer
- selectedLabel
- size
- skeleton
- theme
- type
- value

###### Outputs

- close
- onClose
- selected

###### HostBindings

- class.bx--dropdown__wrapper

###### HostListeners

- keydown

###### Accessors

- appendToBody

### Constructor

`constructor(elementRef: ElementRef, i18n: I18n, dropdownService: DropdownService)`
Defined in src/dropdown/dropdown.component.ts:252

Creates an instance of Dropdown.

Parameters :

Name | Type | Optional
elementRef | `ElementRef` | No
i18n | `I18n` | No
dropdownService | `DropdownService` | No

### Inputs

`appendInline` | set to `true` to place the dropdown view inline with the component Default value : `false`
Defined in src/dropdown/dropdown.component.ts:184

`appendToBody` | Deprecated. Dropdown now defaults to appending inline Set to `true` if the `Dropdown` is to be appended to the DOM body.
Defined in src/dropdown/dropdown.component.ts:171

`disableArrowKeys` | Set to `true` for a dropdown without arrow key activation. Default value : `false`
Defined in src/dropdown/dropdown.component.ts:166

`disabled` | Set to `true` to disable the dropdown. Default value : `false`
Defined in src/dropdown/dropdown.component.ts:154

`displayValue` | The selected value from the `Dropdown`. Can be a string or template. Type : `string | TemplateRef<any>` Default value : `""`
Defined in src/dropdown/dropdown.component.ts:137

`helperText` | Sets the optional helper text. Type : `string | TemplateRef<any>`
Defined in src/dropdown/dropdown.component.ts:129

`id` | Default value : ``dropdown-${Dropdown.dropdownCount++}``
Defined in src/dropdown/dropdown.component.ts:121

`inline` | Set to `true` for an inline dropdown. Default value : `false`
Defined in src/dropdown/dropdown.component.ts:162

`label` | Label for the dropdown. Type : `string | TemplateRef<any>`
Defined in src/dropdown/dropdown.component.ts:125

`menuButtonLabel` | Accessible label for the button that opens the dropdown list. Defaults to the `DROPDOWN.OPEN` value from the i18n service. Default value : `this.i18n.get().DROPDOWN.OPEN`
Defined in src/dropdown/dropdown.component.ts:198

`placeholder` | Value displayed if no item is selected. Default value : `""`
Defined in src/dropdown/dropdown.component.ts:133

`scrollableContainer` | Query string for the element that contains the `Dropdown`. Used to trigger closing the dropdown if it scrolls outside of the viewport of the `scrollableContainer`. Type : `string`
Defined in src/dropdown/dropdown.component.ts:189

`selectedLabel` | Provides the label for the "# selected" text. Defaults to the `DROPDOWN.SELECTED` value from the i18n service. Default value : `this.i18n.get().DROPDOWN.SELECTED`
Defined in src/dropdown/dropdown.component.ts:203

`size` | Size to render the dropdown field. Type : `"sm" | "md" | "lg"` Default value : `"md"`
Defined in src/dropdown/dropdown.component.ts:141

`skeleton` | Set to `true` for a loading dropdown. Default value : `false`
Defined in src/dropdown/dropdown.component.ts:158

`theme` | `light` or `dark` dropdown theme Type : `"light" | "dark"` Default value : `"dark"`
Defined in src/dropdown/dropdown.component.ts:150

`type` | Defines whether or not the `Dropdown` supports selecting multiple items as opposed to single item selection. Type : `"single" | "multi"` Default value : `"single"`
Defined in src/dropdown/dropdown.component.ts:146

`value` | Specifies the property to be used as the return value to `ngModel` Type : `string`
Defined in src/dropdown/dropdown.component.ts:193

### Outputs

`close` | Emits event notifying to other classes that the `Dropdown` has been closed (collapsed). `$event` Type: `EventEmitter<any>`
Defined in src/dropdown/dropdown.component.ts:215

`onClose` | Emits event notifying to other classes that the `Dropdown` has been closed (collapsed). `$event` Type: `EventEmitter<any>`
Defined in src/dropdown/dropdown.component.ts:211

`selected` | Emits selection events. `$event` Type: `EventEmitter<Object>`
Defined in src/dropdown/dropdown.component.ts:207

### HostBindings

class.bx--dropdown__wrapper
`class.bx--dropdown__wrapper: ```
Default value : `true`
Defined in src/dropdown/dropdown.component.ts:230

### HostListeners

keydown
Arguments : `'$event'`
`keydown(event: KeyboardEvent)`
Defined in src/dropdown/dropdown.component.ts:375
Adds keyboard functionality for navigation, selection and closing of the `Dropdown`.

### Methods

_appendToBody
`_appendToBody()`
Defined in src/dropdown/dropdown.component.ts:533
Creates the `Dropdown` list as an element that is appended to the DOM body. Returns : `void`

_appendToDropdown
`_appendToDropdown()`
Defined in src/dropdown/dropdown.component.ts:525
Creates the `Dropdown` list appending it to the dropdown parent object instead of the body. Returns : `void`

_keyboardNav
`_keyboardNav(event: KeyboardEvent)`
Defined in src/dropdown/dropdown.component.ts:504

Handles keyboard events so users are controlling the `Dropdown` instead of unintentionally controlling outside elements.

Parameters :

Name | Type | Optional
event | `KeyboardEvent` | No

Returns : `void`

_noop
`_noop()`
Defined in src/dropdown/dropdown.component.ts:484
Returns : `void`

_outsideClick
`_outsideClick(event)`
Defined in src/dropdown/dropdown.component.ts:488

Handles clicks outside of the `Dropdown`.

Parameters :

Name | Optional
event | No

Returns : `void`

_outsideKey
`_outsideKey(event)`
Defined in src/dropdown/dropdown.component.ts:496

Parameters :

Name | Optional
event | No

Returns : `void`

addScrollEventListener
`addScrollEventListener()`
Defined in src/dropdown/dropdown.component.ts:616
Add scroll event listener if scrollableContainer is provided Returns : `void`

clearSelected
`clearSelected()`
Defined in src/dropdown/dropdown.component.ts:468
Returns : `void`

closedDropdownNavigation
`closedDropdownNavigation(event)`
Defined in src/dropdown/dropdown.component.ts:407

Parameters :

Name | Optional
event | No

Returns : `void`

closeMenu
`closeMenu()`
Defined in src/dropdown/dropdown.component.ts:587
Collapsing the dropdown menu and removing unnecessary `EventListeners`. Returns : `void`

getDisplayStringValue
`getDisplayStringValue()`
Defined in src/dropdown/dropdown.component.ts:427
Returns the display value if there is a selection and displayValue is set, if there is just a selection the ListItem content property will be returned, otherwise the placeholder will be returned. Returns : `Observable<string>`

getRenderTemplateContext
`getRenderTemplateContext()`
Defined in src/dropdown/dropdown.component.ts:448
Returns : `{ items: any; item?: undefined; } | { item: any; items?: undefined; } | { items?: undefined; item...`

getSelectedCount
`getSelectedCount()`
Defined in src/dropdown/dropdown.component.ts:462
Returns : `number`

isRenderString
`isRenderString()`
Defined in src/dropdown/dropdown.component.ts:444
Returns : `boolean`

Public isTemplate
`isTemplate(value)`
Defined in src/dropdown/dropdown.component.ts:669

Parameters :

Name | Optional
value | No

Returns : `boolean`

isVisibleInContainer
`isVisibleInContainer(elem: HTMLElement, container: HTMLElement)`
Defined in src/dropdown/dropdown.component.ts:656

Returns `true` if the `elem` is visible within the `container`.

Parameters :

Name | Type | Optional
elem | `HTMLElement` | No
container | `HTMLElement` | No

Returns : `boolean`

ngAfterContentInit
`ngAfterContentInit()`
Defined in src/dropdown/dropdown.component.ts:272
Initializes classes and subscribes to events for single or multi selection. Returns : `void`

ngOnDestroy
`ngOnDestroy()`
Defined in src/dropdown/dropdown.component.ts:310
Removing the `Dropdown` from the body if it is appended to the body. Returns : `void`

ngOnInit
`ngOnInit()`
Defined in src/dropdown/dropdown.component.ts:263
Updates the `type` property in the `@ContentChild`. The `type` property specifies whether the `Dropdown` allows single selection or multi selection. Returns : `void`

onBlur
`onBlur()`
Defined in src/dropdown/dropdown.component.ts:353
Returns : `void`

openMenu
`openMenu()`
Defined in src/dropdown/dropdown.component.ts:544
Expands the dropdown menu in the view. Returns : `void`

registerOnChange
`registerOnChange(fn: any)`
Defined in src/dropdown/dropdown.component.ts:357

Parameters :

Name | Type | Optional
fn | `any` | No

Returns : `void`

registerOnTouched
`registerOnTouched(fn: any)`
Defined in src/dropdown/dropdown.component.ts:364

Registering the function injected to control the touch use of the `Dropdown`.

Parameters :

Name | Type | Optional
fn | `any` | No

Returns : `void`

removeScrollEventListener
`removeScrollEventListener()`
Defined in src/dropdown/dropdown.component.ts:636
Removes any `EventListeners` responsible for scroll functionality. Returns : `void`

toggleMenu
`toggleMenu()`
Defined in src/dropdown/dropdown.component.ts:645
Controls toggling menu states between open/expanded and closed/collapsed. Returns : `void`

valueSelected
`valueSelected()`
Defined in src/dropdown/dropdown.component.ts:479
Returns `true` if there is a value selected. Returns : `boolean`

writeValue
`writeValue(value: any)`
Defined in src/dropdown/dropdown.component.ts:319

Propagates the injected `value`.

Parameters :

Name | Type | Optional
value | `any` | No

Returns : `void`

### Properties

dropdownButton
`dropdownButton: ```
Decorators : `@ViewChild('dropdownButton')`
Defined in src/dropdown/dropdown.component.ts:224
Maintains a reference to the view DOM element of the `Dropdown` button.

Static dropdownCount
`dropdownCount: `number``
Type : `number`
Default value : `0`
Defined in src/dropdown/dropdown.component.ts:120

dropdownMenu
`dropdownMenu: ```
Decorators : `@ViewChild('dropdownMenu')`
Defined in src/dropdown/dropdown.component.ts:228
ViewChid of the dropdown view.

dropUp
`dropUp: ```
Default value : `false`
Defined in src/dropdown/dropdown.component.ts:239
controls wether the `drop-up` class is applied

keyboardNav
`keyboardNav: ```
Default value : `this._keyboardNav.bind(this)`
Defined in src/dropdown/dropdown.component.ts:246

menuIsClosed
`menuIsClosed: ```
Default value : `true`
Defined in src/dropdown/dropdown.component.ts:234
Set to `true` if the dropdown is closed (not expanded).

noop
`noop: ```
Default value : `this._noop.bind(this)`
Defined in src/dropdown/dropdown.component.ts:243

Protected onTouchedCallback
`onTouchedCallback: `function``
Type : `function`
Default value : `this._noop`
Defined in src/dropdown/dropdown.component.ts:252

outsideClick
`outsideClick: ```
Default value : `this._outsideClick.bind(this)`
Defined in src/dropdown/dropdown.component.ts:244

outsideKey
`outsideKey: ```
Default value : `this._outsideKey.bind(this)`
Defined in src/dropdown/dropdown.component.ts:245

propagateChange
`propagateChange: ```
Default value : `(_: any) => {}`
Defined in src/dropdown/dropdown.component.ts:368

scroll
`scroll: `Subscription``
Type : `Subscription`
Defined in src/dropdown/dropdown.component.ts:250
Maintians an Event Observable Subscription for tracking scrolling within the open `Dropdown` list.

view
`view: `AbstractDropdownView``
Type : `AbstractDropdownView`
Decorators : `@ContentChild(AbstractDropdownView)`
Defined in src/dropdown/dropdown.component.ts:220
Maintains a reference to the `AbstractDropdownView` object within the content DOM.

### Accessors

appendToBody
get `appendToBody()`
Defined in src/dropdown/dropdown.component.ts:178
set `appendToBody(v)`
Defined in src/dropdown/dropdown.component.ts:171

Deprecated. Dropdown now defaults to appending inline Set to `true` if the `Dropdown` is to be appended to the DOM body.

Parameters :

Name | Optional
v | No

Returns : `void`

    import {
    	Component,
    	Input,
    	Output,
    	EventEmitter,
    	ElementRef,
    	ContentChild,
    	OnInit,
    	ViewChild,
    	AfterContentInit,
    	HostListener,
    	OnDestroy,
    	HostBinding,
    	TemplateRef
    } from "@angular/core";
    import { NG_VALUE_ACCESSOR } from "@angular/forms";

    // Observable import is required here so typescript can compile correctly
    import { Observable, fromEvent, of, Subscription } from "rxjs";

    import { AbstractDropdownView } from "./abstract-dropdown-view.class";
    import { position } from "@carbon/utils-position";
    import { I18n } from "./../i18n/i18n.module";
    import { ListItem } from "./list-item.interface";
    import { DropdownService } from "./dropdown.service";

    /**
     * Drop-down lists enable users to select one or more items from a list.
     *
     * [See demo](../../?path=/story/dropdown--basic)
     *
     * <example-url>../../iframe.html?id=dropdown--basic</example-url>
     *
     * @export
     * @class Dropdown
     * @implements {OnInit}
     * @implements {AfterContentInit}
     * @implements {OnDestroy}
     */
    @Component({
    	selector: "ibm-dropdown",
    	template: `
    	<label *ngIf="label" [for]="id" class="bx--label">
    		<ng-container *ngIf="!isTemplate(label)">{{label}}</ng-container>
    		<ng-template *ngIf="isTemplate(label)" [ngTemplateOutlet]="label"></ng-template>
    	</label>
    	<div *ngIf="helperText" class="bx--form__helper-text">
    		<ng-container *ngIf="!isTemplate(helperText)">{{helperText}}</ng-container>
    		<ng-template *ngIf="isTemplate(helperText)" [ngTemplateOutlet]="helperText"></ng-template>
    	</div>
    	<div
    		[id]="id"
    		class="bx--dropdown bx--list-box"
    		[ngClass]="{
    			'bx--dropdown--light': theme === 'light',
    			'bx--list-box--inline': inline,
    			'bx--skeleton': skeleton,
    			'bx--dropdown--disabled bx--list-box--disabled': disabled
    		}">
    		<button
    			type="button"
    			#dropdownButton
    			class="bx--list-box__field"
    			[ngClass]="{'a': !menuIsClosed}"
    			[attr.aria-expanded]="!menuIsClosed"
    			[attr.aria-disabled]="disabled"
    			(click)="toggleMenu()"
    			(blur)="onBlur()"
    			[disabled]="disabled">
    			<div
    				(click)="clearSelected()"
    				*ngIf="type === 'multi' && getSelectedCount() > 0"
    				class="bx--list-box__selection--multi"
    				title="Clear all selected items">
    				{{getSelectedCount()}}
    				<svg
    					focusable="false"
    					preserveAspectRatio="xMidYMid meet"
    					style="will-change: transform;"
    					role="img"
    					xmlns="http://www.w3.org/2000/svg"
    					width="16"
    					height="16"
    					viewBox="0 0 16 16"
    					aria-hidden="true">
    					<path d="M12 4.7l-.7-.7L8 7.3 4.7 4l-.7.7L7.3 8 4 11.3l.7.7L8 8.7l3.3 3.3.7-.7L8.7 8z"></path>
    				</svg>
    			</div>
    			<span *ngIf="isRenderString()" class="bx--list-box__label">{{getDisplayStringValue() | async}}</span>
    			<ng-template
    				*ngIf="!isRenderString()"
    				[ngTemplateOutletContext]="getRenderTemplateContext()"
    				[ngTemplateOutlet]="displayValue">
    			</ng-template>
    			<ibm-icon-chevron-down16
    				*ngIf="!skeleton"
    				class="bx--list-box__menu-icon"
    				[attr.aria-label]="menuButtonLabel"
    				[ngClass]="{'bx--list-box__menu-icon--open': !menuIsClosed }">
    			</ibm-icon-chevron-down16>
    		</button>
    		<div
    			#dropdownMenu
    			[ngClass]="{
    				'drop-up': dropUp
    			}">
    			<ng-content *ngIf="!menuIsClosed"></ng-content>
    		</div>
    	</div>
    	`,
    	providers: [
    		{
    			provide: NG_VALUE_ACCESSOR,
    			useExisting: Dropdown,
    			multi: true
    		}
    	]
    })
    export class Dropdown implements OnInit, AfterContentInit, OnDestroy {
    	static dropdownCount = 0;
    	@Input() id = `dropdown-${Dropdown.dropdownCount++}`;
    	/**
    	 * Label for the dropdown.
    	 */
    	@Input() label: string | TemplateRef<any>;
    	/**
    	 * Sets the optional helper text.
    	 */
    	@Input() helperText: string | TemplateRef<any>;
    	/**
    	 * Value displayed if no item is selected.
    	 */
    	@Input() placeholder = "";
    	/**
    	 * The selected value from the `Dropdown`. Can be a string or template.
    	 */
    	@Input() displayValue: string | TemplateRef<any> = "";
    	/**
    	 * Size to render the dropdown field.
    	 */
    	@Input() size: "sm" | "md" | "lg" = "md";
    	/**
    	 * Defines whether or not the `Dropdown` supports selecting multiple items as opposed to single
    	 * item selection.
    	 */
    	@Input() type: "single" | "multi" = "single";
    	/**
    	 * `light` or `dark` dropdown theme
    	 */
    	@Input() theme: "light" | "dark" = "dark";
    	/**
    	 * Set to `true` to disable the dropdown.
    	 */
    	@Input() disabled = false;
    	/**
    	 * Set to `true` for a loading dropdown.
    	 */
    	@Input() skeleton = false;
    	/**
    	 * Set to `true` for an inline dropdown.
    	 */
    	@Input() inline = false;
    	/**
    	 * Set to `true` for a dropdown without arrow key activation.
    	 */
    	@Input() disableArrowKeys = false;
    	/**
    	 * Deprecated. Dropdown now defaults to appending inline
    	 * Set to `true` if the `Dropdown` is to be appended to the DOM body.
    	 */
    	@Input() set appendToBody (v) {
    		console.log("`appendToBody` has been deprecated. Dropdowns now append to the body by default.");
    		console.log("Ensure you have an `ibm-placeholder` in your app.");
    		console.log("Use `appendInline` if you need to position your dropdowns within the normal page flow.");
    		this.appendInline = !v;
    	}

    	get appendToBody() {
    		return !this.appendInline;
    	}
    	/**
    	 * set to `true` to place the dropdown view inline with the component
    	 */
    	@Input() appendInline = false;
    	/**
    	 * Query string for the element that contains the `Dropdown`.
    	 * Used to trigger closing the dropdown if it scrolls outside of the viewport of the `scrollableContainer`.
    	 */
    	@Input() scrollableContainer: string;
    	/**
    	 * Specifies the property to be used as the return value to `ngModel`
    	 */
    	@Input() value: string;
    	/**
    	 * Accessible label for the button that opens the dropdown list.
    	 * Defaults to the `DROPDOWN.OPEN` value from the i18n service.
    	 */
    	@Input() menuButtonLabel = this.i18n.get().DROPDOWN.OPEN;
    	/**
    	 * Provides the label for the "# selected" text.
    	 * Defaults to the `DROPDOWN.SELECTED` value from the i18n service.
    	 */
    	@Input() selectedLabel = this.i18n.get().DROPDOWN.SELECTED;
    	/**
    	 * Emits selection events.
    	 */
    	@Output() selected: EventEmitter<Object> = new EventEmitter<Object>();
    	/**
    	 * Emits event notifying to other classes that the `Dropdown` has been closed (collapsed).
    	 */
    	@Output() onClose: EventEmitter<any> = new EventEmitter<any>();
    	/**
    	 * Emits event notifying to other classes that the `Dropdown` has been closed (collapsed).
    	 */
    	@Output() close: EventEmitter<any> = new EventEmitter<any>();

    	/**
    	 * Maintains a reference to the `AbstractDropdownView` object within the content DOM.
    	 */
    	@ContentChild(AbstractDropdownView) view: AbstractDropdownView;
    	/**
    	 * Maintains a reference to the view DOM element of the `Dropdown` button.
    	 */
    	@ViewChild("dropdownButton") dropdownButton;
    	/**
    	 * ViewChid of the dropdown view.
    	 */
    	@ViewChild("dropdownMenu") dropdownMenu;

    	@HostBinding("class.bx--dropdown__wrapper") hostClass = true;
    	/**
    	 * Set to `true` if the dropdown is closed (not expanded).
    	 */
    	menuIsClosed = true;

    	/**
    	 * controls wether the `drop-up` class is applied
    	 */
    	dropUp = false;

    	// .bind creates a new function, so we declare the methods below
    	// but .bind them up here
    	noop = this._noop.bind(this);
    	outsideClick = this._outsideClick.bind(this);
    	outsideKey = this._outsideKey.bind(this);
    	keyboardNav = this._keyboardNav.bind(this);
    	/**
    	 *  Maintians an Event Observable Subscription for tracking scrolling within the open `Dropdown` list.
    	 */
    	scroll: Subscription;

    	protected onTouchedCallback: () => void = this._noop;

    	/**
    	 * Creates an instance of Dropdown.
    	 */
    	constructor(protected elementRef: ElementRef, protected i18n: I18n, protected dropdownService: DropdownService) {}

    	/**
    	 * Updates the `type` property in the `@ContentChild`.
    	 * The `type` property specifies whether the `Dropdown` allows single selection or multi selection.
    	 */
    	ngOnInit() {
    		if (this.view) {
    			this.view.type = this.type;
    		}
    	}

    	/**
    	 * Initializes classes and subscribes to events for single or multi selection.
    	 */
    	ngAfterContentInit() {
    		if (!this.view) {
    			return;
    		}
    		this.view.type = this.type;
    		this.view.size = this.size;
    		this.view.select.subscribe(event => {
    			if (this.type === "multi") {
    				// if we have a `value` selector and selected items map them approperiatly
    				if (this.value && this.view.getSelected()) {
    					const values = this.view.getSelected().map(item => item[this.value]);
    					this.propagateChange(values);
    				// otherwise just pass up the values from `getSelected`
    				} else {
    					this.propagateChange(this.view.getSelected());
    				}
    			} else {
    				this.closeMenu();
    				if (event.item && event.item.selected) {
    					if (this.value) {
    						this.propagateChange(event.item[this.value]);
    					} else {
    						this.propagateChange(event.item);
    					}
    				} else {
    					this.propagateChange(null);
    				}
    			}
    			// only emit selected for "organic" selections
    			if (!event.isUpdate) {
    				this.selected.emit(event);
    			}
    		});
    	}

    	/**
    	 * Removing the `Dropdown` from the body if it is appended to the body.
    	 */
    	ngOnDestroy() {
    		if (this.appendToBody) {
    			this._appendToDropdown();
    		}
    	}

    	/**
    	 * Propagates the injected `value`.
    	 */
    	writeValue(value: any) {
    		// propagate null/falsey as an array (deselect everything)
    		if (!value) {
    			this.view.propagateSelected([value]);
    		} else if (this.type === "single") {
    			if (this.value) {
    				// clone the specified item and update its state
    				const newValue = Object.assign({}, this.view.getListItems().find(item => item[this.value] === value));
    				newValue.selected = true;
    				this.view.propagateSelected([newValue]);
    			} else {
    				// pass the singular value as an array of ListItem
    				this.view.propagateSelected([value]);
    			}
    		} else {
    			if (this.value) {
    				// clone the items and update their state based on the received value array
    				// this way we don't lose any additional metadata that may be passed in view the `items` Input
    				const newValues = Array.from(this.view.getListItems(), item => Object.assign({}, item));
    				for (const v of value) {
    					for (const newValue of newValues) {
    						if (newValue[this.value] === v) {
    							newValue.selected = true;
    						}
    					}
    				}
    				this.view.propagateSelected(newValues);
    			} else {
    				// we can safely assume we're passing an array of `ListItem`s
    				this.view.propagateSelected(value);
    			}
    		}
    	}

    	onBlur() {
    		this.onTouchedCallback();
    	}

    	registerOnChange(fn: any) {
    		this.propagateChange = fn;
    	}

    	/**
    	 * Registering the function injected to control the touch use of the `Dropdown`.
    	 */
    	registerOnTouched(fn: any) {
    		this.onTouchedCallback = fn;
    	}

    	propagateChange = (_: any) => {};

    	/**
    	 * Adds keyboard functionality for navigation, selection and closing of the `Dropdown`.
    	 */
    	@HostListener("keydown", ["$event"])
    	// "Esc", "Spacebar", "Down", and "Up" are IE specific values
    	onKeyDown(event: KeyboardEvent) {
    		if ((event.key === "Escape" || event.key === "Esc") && !this.menuIsClosed) {
    			event.stopImmediatePropagation();  // don't unintentionally close other widgets that listen for Escape
    		}
    		if (event.key === "Escape" || event.key === "Esc") {
    			event.preventDefault();
    			this.closeMenu();
    			this.dropdownButton.nativeElement.focus();
    		} else if (this.menuIsClosed && (event.key === " " || event.key === "ArrowDown" || event.key === "ArrowUp" ||
    			event.key === "Spacebar" || event.key === "Down" || event.key === "Up")) {
    			if (this.disableArrowKeys && (event.key === "ArrowDown" || event.key === "ArrowUp" || event.key === "Down" || event.key === "Up")) {
    				return;
    			}
    			event.preventDefault();
    			this.openMenu();
    		}

    		if (!this.menuIsClosed && event.key === "Tab" && this.dropdownMenu.nativeElement.contains(event.target as Node)) {
    			this.closeMenu();
    		}

    		if (!this.menuIsClosed && event.key === "Tab" && event.shiftKey) {
    			this.closeMenu();
    		}

    		if (this.type === "multi") { return; }

    		if (this.menuIsClosed) {
    			this.closedDropdownNavigation(event);
    		}
    	}

    	closedDropdownNavigation(event) {
    		// "Down", and "Up" are IE specific values
    		if (event.key === "ArrowDown" || event.key === "Down") {
    			event.preventDefault();
    			this.view.getCurrentItem().selected = false;
    			let item = this.view.getNextItem();
    			if (item) { item.selected = true; }
    		} else if (event.key === "ArrowUp" || event.key === "Up") {
    			event.preventDefault();
    			this.view.getCurrentItem().selected = false;
    			let item = this.view.getPrevItem();
    			if (item) { item.selected = true; }
    		}
    	}

    	/**
    	 * Returns the display value if there is a selection and displayValue is set,
    	 * if there is just a selection the ListItem content property will be returned,
    	 * otherwise the placeholder will be returned.
    	 */
    	getDisplayStringValue(): Observable<string> {
    		if (!this.view) {
    			return;
    		}
    		let selected = this.view.getSelected();
    		if (selected && (!this.displayValue || !this.isRenderString())) {
    			if (this.type === "multi") {
    				return of(this.placeholder);
    			} else {
    				return of(selected[0].content);
    			}
    		} else if (selected && this.isRenderString()) {
    			return of(this.displayValue as string);
    		}
    		return of(this.placeholder);
    	}

    	isRenderString(): boolean {
    		return typeof this.displayValue === "string";
    	}

    	getRenderTemplateContext() {
    		if (!this.view) {
    			return;
    		}
    		let selected = this.view.getSelected();
    		if (this.type === "multi") {
    			return {items: selected};
    		} else if (selected && selected.length > 0) {
    			return {item: selected[0]}; // this is to be compatible with the dropdown-list template
    		} else {
    			return {};
    		}
    	}

    	getSelectedCount(): number {
    		if (this.view.getSelected()) {
    			return this.view.getSelected().length;
    		}
    	}

    	clearSelected() {
    		for (const item of this.view.getListItems()) {
    			item.selected = false;
    		}
    		this.selected.emit([]);
    		this.propagateChange([]);
    	}

    	/**
    	 * Returns `true` if there is a value selected.
    	 */
    	valueSelected(): boolean {
    		if (this.view.getSelected()) { return true; }
    		return false;
    	}

    	_noop() {}
    	/**
    	 * Handles clicks outside of the `Dropdown`.
    	 */
    	_outsideClick(event) {
    		if (!this.elementRef.nativeElement.contains(event.target) &&
    			// if we're appendToBody the list isn't within the _elementRef,
    			// so we've got to check if our target is possibly in there too.
    			!this.dropdownMenu.nativeElement.contains(event.target)) {
    			this.closeMenu();
    		}
    	}
    	_outsideKey(event) {
    		if (!this.menuIsClosed && event.key === "Tab" && this.dropdownMenu.nativeElement.contains(event.target as Node)) {
    			this.closeMenu();
    		}
    	}
    	/**
    	 * Handles keyboard events so users are controlling the `Dropdown` instead of unintentionally controlling outside elements.
    	 */
    	_keyboardNav(event: KeyboardEvent) {
    		// "Esc" is an IE specific value
    		if ((event.key === "Escape" || event.key === "Esc") && !this.menuIsClosed) {
    			event.stopImmediatePropagation();  // don't unintentionally close modal if inside of it
    		}
    		if (event.key === "Escape" || event.key === "Esc") {
    			event.preventDefault();
    			this.closeMenu();
    			this.dropdownButton.nativeElement.focus();
    		} else if (!this.menuIsClosed && event.key === "Tab") {
    			// this way focus will start on the next focusable item from the dropdown
    			// not the top of the body!
    			this.dropdownButton.nativeElement.focus();
    			this.dropdownButton.nativeElement.dispatchEvent(new KeyboardEvent("keydown", {bubbles: true, cancelable: true, key: "Tab"}));
    			this.closeMenu();
    		}
    	}

    	/**
    	 * Creates the `Dropdown` list appending it to the dropdown parent object instead of the body.
    	 */
    	_appendToDropdown() {
    		this.dropdownService.appendToDropdown(this.elementRef.nativeElement);
    		this.dropdownMenu.nativeElement.removeEventListener("keydown", this.keyboardNav, true);
    	}

    	/**
    	 * Creates the `Dropdown` list as an element that is appended to the DOM body.
    	 */
    	_appendToBody() {
    		this.dropdownService.appendToBody(
    			this.dropdownButton.nativeElement,
    			this.dropdownMenu.nativeElement,
    			this.elementRef.nativeElement.className);
    		this.dropdownMenu.nativeElement.addEventListener("keydown", this.keyboardNav, true);
    	}

    	/**
    	 * Expands the dropdown menu in the view.
    	 */
    	openMenu() {
    		this.menuIsClosed = false;

    		// move the dropdown list to the body if we're not appending inline
    		// and position it relative to the dropdown wrapper
    		if (!this.appendInline) {
    			this.addScrollEventListener();
    			this._appendToBody();
    		}

    		// set the dropdown menu to drop up if it's near the bottom of the screen
    		// setTimeout lets us measure after it's visible in the DOM
    		setTimeout(() => {
    			const menu = this.dropdownMenu.nativeElement;
    			const boundingClientRect = menu.getBoundingClientRect();

    			if (boundingClientRect.bottom > window.innerHeight) {
    				// min height of 100px
    				if (window.innerHeight - boundingClientRect.top > 100) {
    					// remove the conditional once this api is settled and part of abstract-dropdown-view.class
    					if (this.view["enableScroll"]) {
    						this.view["enableScroll"]();
    					}
    				} else {
    					this.dropUp = true;
    				}
    			} else {
    				this.dropUp = false;
    			}
    		}, 0);

    		// we bind noop to document.body.firstElementChild to allow safari to fire events
    		// from document. Then we unbind everything later to keep things light.
    		document.body.firstElementChild.addEventListener("click", this.noop, true);
    		document.body.firstElementChild.addEventListener("keydown", this.noop, true);
    		document.addEventListener("click", this.outsideClick, true);
    		document.addEventListener("keydown", this.outsideKey, true);
    		setTimeout(() => this.view.initFocus(), 0);
    	}

    	/**
    	 * Collapsing the dropdown menu and removing unnecessary `EventListeners`.
    	 */
    	closeMenu() {
    		// return early if the menu is already closed
    		if (this.menuIsClosed) { return; }
    		this.menuIsClosed = true;
    		this.onClose.emit();
    		this.close.emit();

    		// focus the trigger button when we close ...
    		this.dropdownButton.nativeElement.focus();

    		// remove the conditional once this api is settled and part of abstract-dropdown-view.class
    		if (this.view["disableScroll"]) {
    			this.view["disableScroll"]();
    		}

    		// move the list back in the component on close
    		if (!this.appendInline) {
    			this.removeScrollEventListener();
    			this._appendToDropdown();
    		}
    		document.body.firstElementChild.removeEventListener("click", this.noop, true);
    		document.body.firstElementChild.removeEventListener("keydown", this.noop, true);
    		document.removeEventListener("click", this.outsideClick, true);
    		document.removeEventListener("keydown", this.outsideKey, true);
    	}

    	/**
    	 * Add scroll event listener if scrollableContainer is provided
    	 */
    	addScrollEventListener() {
    		if (this.scrollableContainer) {
    			const container: HTMLElement = document.querySelector(this.scrollableContainer);

    			if (container) {
    				this.scroll = fromEvent(container, "scroll")
    				.subscribe(() => {
    					if (this.isVisibleInContainer(this.elementRef.nativeElement, container)) {
    						this.dropdownService.updatePosition(this.dropdownButton.nativeElement);
    					} else {
    						this.closeMenu();
    					}
    				});
    			}
    		}
    	}

    	/**
    	 * Removes any `EventListeners` responsible for scroll functionality.
    	 */
    	removeScrollEventListener() {
    		if (this.scroll) {
    			this.scroll.unsubscribe();
    		}
    	}

    	/**
    	 * Controls toggling menu states between open/expanded and closed/collapsed.
    	 */
    	toggleMenu() {
    		if (this.menuIsClosed) {
    			this.openMenu();
    		} else {
    			this.closeMenu();
    		}
    	}

    	/**
    	 * Returns `true` if the `elem` is visible within the `container`.
    	 */
    	isVisibleInContainer(elem: HTMLElement, container: HTMLElement): boolean {
    		const containerTop = container.scrollTop;
    		const containerBottom = containerTop + container.offsetHeight;
    		const elemTop = elem.offsetTop + elem.offsetHeight;
    		const elemBottom = elemTop;

    		if ((elemBottom <= containerBottom) && (elemTop >= containerTop)) {
    			return true;
    		}

    		return false;
    	}

    	public isTemplate(value) {
    		return value instanceof TemplateRef;
    	}
    }

Legend
Html element
Component
Html element with directive

# result-matching ""

# No results matching ""