### 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` `ControlValueAccessor`

### 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,
    		'bx--dropdown--invalid': invalid
    	}">
    	<div
    		type="button"
    		#dropdownButton
    		class="bx--list-box__field"
    		[ngClass]="{'a': !menuIsClosed}"
    		[attr.aria-expanded]="!menuIsClosed"
    		[attr.aria-disabled]="disabled"
    		(click)="disabled ? $event.stopPropagation() : toggleMenu()"
    		(blur)="onBlur()"
    		[attr.disabled]="disabled ? true : null"
    		[tabindex]="disabled ? -1 : 0">
    		<div
    			(click)="clearSelected()"
    			*ngIf="type === 'multi' && getSelectedCount() > 0"
    			class="bx--list-box__selection--multi"
    			[title]="clearText">
    			{{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>
    		<svg ibmIconWarningFilled16
    			*ngIf="invalid"
    			class="bx--dropdown__invalid-icon">
    		</svg>
    		<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>
    	</div>
    	<div
    		#dropdownMenu
    		[ngClass]="{
    			'drop-up': dropUp
    		}">
    		<ng-content *ngIf="!menuIsClosed"></ng-content>
    	</div>
    </div>
    <div *ngIf="invalid" class="bx--form-requirement">
    	{{invalidText}}
    </div>

### Index

###### Properties
dropdownButton Static dropdownCount dropdownMenu dropUp keyboardNav menuIsClosed noop Protected onTouchedCallback outsideClick outsideKey propagateChange view Protected visibilitySubscription Protected writtenValue
###### Methods
_appendToBody _appendToDropdown _keyboardNav _noop _outsideClick _outsideKey clearSelected closedDropdownNavigation closeMenu getDisplayStringValue getRenderTemplateContext getSelectedCount isRenderString Public isTemplate ngAfterContentInit ngOnDestroy ngOnInit onBlur openMenu registerOnChange registerOnTouched setDisabledState toggleMenu valueSelected writeValue
###### Inputs
appendInline appendToBody clearText disableArrowKeys disabled displayValue helperText id inline invalid invalidText 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, appRef: ApplicationRef, elementService: ElementService)`
Defined in src/dropdown/dropdown.component.ts:273
Creates an instance of Dropdown. Parameters :
Name | Type | Optional
elementRef | `ElementRef` | No
i18n | `I18n` | No
dropdownService | `DropdownService` | No
appRef | `ApplicationRef` | No
elementService | `ElementService` | 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:204

`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:191

`clearText` | Sets the optional clear button tooltip text. Type : `string` Default value : `this.i18n.get().DROPDOWN.CLEAR`
Defined in src/dropdown/dropdown.component.ts:149

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

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

`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:145

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

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

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

`invalid` | Set to `true` for invalid state. Default value : `false`
Defined in src/dropdown/dropdown.component.ts:182

`invalidText` | Value displayed if dropdown is in invalid state. Default value : `""`
Defined in src/dropdown/dropdown.component.ts:186

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

`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:218

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

`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:209

`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:223

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

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

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

`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:158

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

### 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:235

`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:231

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

### HostBindings

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

### HostListeners

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

### Methods

_appendToBody
`_appendToBody()`
Defined in src/dropdown/dropdown.component.ts:581
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:573
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:552
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:532
Returns : `void`

_outsideClick
`_outsideClick(event)`
Defined in src/dropdown/dropdown.component.ts:536
Handles clicks outside of the `Dropdown`. Parameters :
Name | Optional
event | No
Returns : `void`

_outsideKey
`_outsideKey(event)`
Defined in src/dropdown/dropdown.component.ts:544
Parameters :
Name | Optional
event | No
Returns : `void`

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

closedDropdownNavigation
`closedDropdownNavigation(event)`
Defined in src/dropdown/dropdown.component.ts:454
Parameters :
Name | Optional
event | No
Returns : `void`

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

getDisplayStringValue
`getDisplayStringValue()`
Defined in src/dropdown/dropdown.component.ts:474
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:495
Returns : `{ items: any; item?: undefined; } | { item: any; items?: undefined; } | { items?: undefined; item...`

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

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

Public isTemplate
`isTemplate(value)`
Defined in src/dropdown/dropdown.component.ts:687
Parameters :
Name | Optional
value | No
Returns : `boolean`

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

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

ngOnInit
`ngOnInit()`
Defined in src/dropdown/dropdown.component.ts:289
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:386
Returns : `void`

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

registerOnChange
`registerOnChange(fn: any)`
Defined in src/dropdown/dropdown.component.ts:390
Parameters :
Name | Type | Optional
fn | `any` | No
Returns : `void`

registerOnTouched
`registerOnTouched(fn: any)`
Defined in src/dropdown/dropdown.component.ts:397
Registering the function injected to control the touch use of the `Dropdown`. Parameters :
Name | Type | Optional
fn | `any` | No
Returns : `void`

setDisabledState
`setDisabledState(isDisabled: boolean)`
Defined in src/dropdown/dropdown.component.ts:413
`ControlValueAccessor` method to programmatically disable the dropdown. ex: `this.formGroup.get("myDropdown").disable();` Parameters :
Name | Type | Optional | Description
isDisabled | `boolean` | No | ``true` to disable the input`
Returns : `void`

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

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

writeValue
`writeValue(value: any)`
Defined in src/dropdown/dropdown.component.ts:350
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:244
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:128

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

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

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

menuIsClosed
`menuIsClosed: ```
Default value : `true`
Defined in src/dropdown/dropdown.component.ts:254
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:263

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

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

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

propagateChange
`propagateChange: ```
Default value : `(_: any) => {}`
Defined in src/dropdown/dropdown.component.ts:404
function passed in by `registerOnChange`

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

Protected visibilitySubscription
`visibilitySubscription: ```
Default value : `new Subscription()`
Defined in src/dropdown/dropdown.component.ts:268

Protected writtenValue
`writtenValue: `[]``
Type : `[]`
Default value : `[]`
Defined in src/dropdown/dropdown.component.ts:273

### Accessors

appendToBody
get `appendToBody()`
Defined in src/dropdown/dropdown.component.ts:198
set `appendToBody(v)`
Defined in src/dropdown/dropdown.component.ts:191
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,
    	ApplicationRef
    } from "@angular/core";
    import { NG_VALUE_ACCESSOR, ControlValueAccessor } from "@angular/forms";

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

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

    /**
     * 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>
     */
    @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,
    			'bx--dropdown--invalid': invalid
    		}">
    		<div
    			type="button"
    			#dropdownButton
    			class="bx--list-box__field"
    			[ngClass]="{'a': !menuIsClosed}"
    			[attr.aria-expanded]="!menuIsClosed"
    			[attr.aria-disabled]="disabled"
    			(click)="disabled ? $event.stopPropagation() : toggleMenu()"
    			(blur)="onBlur()"
    			[attr.disabled]="disabled ? true : null"
    			[tabindex]="disabled ? -1 : 0">
    			<div
    				(click)="clearSelected()"
    				*ngIf="type === 'multi' && getSelectedCount() > 0"
    				class="bx--list-box__selection--multi"
    				[title]="clearText">
    				{{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>
    			<svg ibmIconWarningFilled16
    				*ngIf="invalid"
    				class="bx--dropdown__invalid-icon">
    			</svg>
    			<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>
    		</div>
    		<div
    			#dropdownMenu
    			[ngClass]="{
    				'drop-up': dropUp
    			}">
    			<ng-content *ngIf="!menuIsClosed"></ng-content>
    		</div>
    	</div>
    	<div *ngIf="invalid" class="bx--form-requirement">
    		{{invalidText}}
    	</div>
    	`,
    	providers: [
    		{
    			provide: NG_VALUE_ACCESSOR,
    			useExisting: Dropdown,
    			multi: true
    		}
    	]
    })
    export class Dropdown implements OnInit, AfterContentInit, OnDestroy, ControlValueAccessor {
    	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> = "";
    	/**
    	 * Sets the optional clear button tooltip text.
    	 */
    	@Input() clearText: string = this.i18n.get().DROPDOWN.CLEAR;
    	/**
    	 * 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;
    	/**
    	 * Set to `true` for invalid state.
    	 */
    	@Input() invalid = false;
    	/**
    	 * Value displayed if dropdown is in invalid state.
    	 */
    	@Input() invalidText = "";
    	/**
    	 * 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.warn("`appendToBody` has been deprecated. Dropdowns now append to the body by default.");
    		console.warn("Ensure you have an `ibm-placeholder` in your app.");
    		console.warn("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);

    	protected visibilitySubscription = new Subscription();

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

    	// primarily used to capture and propagate input to `writeValue` before the content is available
    	protected writtenValue = [];

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

    	/**
    	 * 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;
    		}
    		if (this.writtenValue && this.writtenValue.length) {
    			this.writeValue(this.writtenValue);
    		}
    		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 appropriately
    				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 && !event.isUpdate) {
    				this.selected.emit(event);
    			}
    			// manually tick the app so the view picks up any changes
    			this.appRef.tick();
    		});
    	}

    	/**
    	 * 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) {
    		// cache the written value so we can use it in `AfterContentInit`
    		this.writtenValue = value;
    		// 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 via the `items` Input
    				let newValues = [];
    				for (const v of value) {
    					for (const item of this.view.getListItems()) {
    						if (item[this.value] === v) {
    							newValues.push(Object.assign({}, item, { 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;
    	}

    	/**
    	 * function passed in by `registerOnChange`
    	 */
    	propagateChange = (_: any) => {};

    	/**
    	 * `ControlValueAccessor` method to programmatically disable the dropdown.
    	 *
    	 * ex: `this.formGroup.get("myDropdown").disable();`
    	 *
    	 * @param isDisabled `true` to disable the input
    	 */
    	setDisabledState(isDisabled: boolean) {
    		this.disabled = isDisabled;
    	}

    	/**
    	 * 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.length && (!this.displayValue || !this.isRenderString())) {
    			if (this.type === "multi") {
    				return of(this.placeholder);
    			} else {
    				return of(selected[0].content);
    			}
    		} else if (selected.length && 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() {
    		if (this.disabled) { return; }
    		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() {
    		// prevents the dropdown from opening when list of items is empty
    		if (this.view.getListItems().length === 0) {
    			return;
    		}

    		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) {
    			const target = this.dropdownButton.nativeElement;
    			const parent = this.elementRef.nativeElement;
    			this.visibilitySubscription = this.elementService
    				.visibility(target, parent)
    				.subscribe(value => {
    					this.dropdownService.updatePosition(this.dropdownButton.nativeElement);
    					if (!value.visible) {
    						this.closeMenu();
    					}
    				}
    			);
    			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.visibilitySubscription.unsubscribe();
    			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);
    	}

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

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

Legend
Html element
Component
Html element with directive

# result-matching ""

# No results matching ""