import {ElementRef, Component, Directive, Host, HostBinding, HostListener, ViewChild, Input, Output, EventEmitter, Optional} from 'angular2/core'; import {NgIf, NgClass, NgControl, FORM_DIRECTIVES} from 'angular2/common'; import {Ion} from '../ion'; import {Config} from '../../config/config'; import {Icon} from '../icon/icon'; import {Button} from '../button/button'; import {isPresent, debounce} from '../../util/util'; /** * @private */ @Directive({ selector: '.searchbar-input', }) export class SearchbarInput { @HostListener('input', ['$event']) /** * @private * Don't send the input's input event */ private stopInput(ev) { ev.preventDefault(); ev.stopPropagation(); } constructor(private _elementRef: ElementRef) {} } /** * @name Searchbar * @module ionic * @description * Manages the display of a Searchbar which can be used to search or filter items. * * @usage * ```html * * * ``` * * @demo /docs/v2/demos/searchbar/ * @see {@link /docs/v2/components#searchbar Searchbar Component Docs} */ @Component({ selector: 'ion-searchbar', host: { '[class.searchbar-has-value]': 'value', '[class.searchbar-hide-cancel]': 'hideCancelButton' }, template: '
' + '' + '
' + '' + '' + '
' + '', directives: [FORM_DIRECTIVES, NgIf, NgClass, Icon, Button, SearchbarInput] }) export class Searchbar extends Ion { private _tmr: any; /** * @private */ @ViewChild(SearchbarInput) searchbarInput; /** * @input {string} Sets the cancel button text to the value passed in */ @Input() cancelButtonText: string; /** * @input {boolean} Hides the cancel button */ @Input() hideCancelButton: any; /** * @input {number} How long, in milliseconds, to wait to trigger the `input` event after each keystroke. Default `250`. */ @Input() debounce: number = 250; /** * @input {string} Sets input placeholder to the value passed in */ @Input() placeholder: string; /** * @input {any} Expression to evaluate when the Searchbar input has changed including cleared */ @Input() ngModel: any; /** * @output {event} When the Searchbar input has changed including cleared */ @Output() input: EventEmitter = new EventEmitter(); /** * @output {event} When the Searchbar input has blurred */ @Output() blur: EventEmitter = new EventEmitter(); /** * @output {event} When the Searchbar input has focused */ @Output() focus: EventEmitter = new EventEmitter(); /** * @output {event} When the cancel button is clicked */ @Output() cancel: EventEmitter = new EventEmitter(); /** * @output {event} When the clear input button is clicked */ @Output() clear: EventEmitter = new EventEmitter(); /** * @private */ value: string = ''; /** * @private */ blurInput: boolean = true; /** * @private */ inputElement: any; /** * @private */ searchIconElement: any; /** * @private */ mode: string; /** * @private */ @HostBinding('class.searchbar-focused') isFocused; /** * @private */ @HostBinding('class.searchbar-left-aligned') shouldLeftAlign; constructor( private _elementRef: ElementRef, private _config: Config, @Optional() ngControl: NgControl ) { super(_elementRef); // If the user passed a ngControl we need to set the valueAccessor if (ngControl) { ngControl.valueAccessor = this; } } /** * @private * On Initialization check for attributes */ ngOnInit() { this.mode = this._config.get('mode'); let hideCancelButton = this.hideCancelButton; if (typeof hideCancelButton === 'string') { this.hideCancelButton = (hideCancelButton === '' || hideCancelButton === 'true'); } this.cancelButtonText = this.cancelButtonText || 'Cancel'; this.placeholder = this.placeholder || 'Search'; if (this.ngModel) this.value = this.ngModel; this.onChange(this.value); this.shouldLeftAlign = this.value && this.value.trim() !== ''; // Using querySelector instead of searchbarInput because at this point it doesn't exist this.inputElement = this._elementRef.nativeElement.querySelector('.searchbar-input'); this.searchIconElement = this._elementRef.nativeElement.querySelector('.searchbar-search-icon'); this.setElementLeft(); } /** * @private * After View Initialization check the value */ ngAfterViewInit() { // If the user passes an undefined variable to ngModel this will warn // and set the value to an empty string if (!isPresent(this.value)) { console.warn('Searchbar was passed an undefined value in ngModel. Please make sure the variable is defined.'); this.value = ''; this.onChange(this.value); } } /** * @private * Determines whether or not to add style to the element * to center it properly (ios only) */ setElementLeft() { if (this.mode !== 'ios') return; if (this.shouldLeftAlign) { this.inputElement.removeAttribute('style'); this.searchIconElement.removeAttribute('style'); } else { this.addElementLeft(); } } /** * @private * Calculates the amount of padding/margin left for the elements * in order to center them based on the placeholder width */ addElementLeft() { // Create a dummy span to get the placeholder width let tempSpan = document.createElement('span'); tempSpan.innerHTML = this.placeholder; document.body.appendChild(tempSpan); // Get the width of the span then remove it let textWidth = tempSpan.offsetWidth; tempSpan.remove(); // Set the input padding left let inputLeft = 'calc(50% - ' + (textWidth / 2) + 'px)'; this.inputElement.style.paddingLeft = inputLeft; // Set the icon margin left let iconLeft = 'calc(50% - ' + ((textWidth / 2) + 30) + 'px)'; this.searchIconElement.style.marginLeft = iconLeft; } /** * @private * Update the Searchbar input value when the input changes */ inputChanged(ev) { let value = ev.target.value; clearTimeout(this._tmr); this._tmr = setTimeout(() => { this.value = value; this.onChange(value); this.input.emit(this); }, Math.round(this.debounce)); } /** * @private * Sets the Searchbar to focused and aligned left on input focus. */ inputFocused() { this.focus.emit(this); this.isFocused = true; this.shouldLeftAlign = true; this.setElementLeft(); } /** * @private * Sets the Searchbar to not focused and checks if it should align left * based on whether there is a value in the searchbar or not. */ inputBlurred() { // blurInput determines if it should blur // if we are clearing the input we still want to stay focused in the input if (this.blurInput === false) { this.searchbarInput._elementRef.nativeElement.focus(); this.blurInput = true; return; } this.blur.emit(this); this.isFocused = false; this.shouldLeftAlign = this.value && this.value.trim() !== ''; this.setElementLeft(); } /** * @private * Clears the input field and triggers the control change. */ clearInput() { this.clear.emit(this); this.value = ''; this.onChange(this.value); this.input.emit(this); this.blurInput = false; } /** * @private * Clears the input field and tells the input to blur since * the clearInput function doesn't want the input to blur * then calls the custom cancel function if the user passed one in. */ cancelSearchbar() { this.cancel.emit(this); this.clearInput(); this.blurInput = true; } /** * @private * Write a new value to the element. */ writeValue(value: any) { this.value = value; } /** * @private */ onChange = (_: any) => {}; /** * @private */ onTouched = () => {}; /** * @private * Set the function to be called when the control receives a change event. */ registerOnChange(fn: (_: any) => {}): void { this.onChange = fn; } /** * @private * Set the function to be called when the control receives a touch event. */ registerOnTouched(fn: () => {}): void { this.onTouched = fn; } }