Last active
February 16, 2016 11:34
-
-
Save vladimir-ivanov/c45809584c1ab07eb3f3 to your computer and use it in GitHub Desktop.
ng2-material md-input fix for when a value is being applied programmatically (e.g. via [value]="myValue") without typing in the input breaks the md-input-has-value class etc
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import { | |
| Directive, | |
| View, | |
| Component, | |
| AfterContentInit, | |
| ElementRef, | |
| OnChanges, | |
| ContentChild, | |
| Input, | |
| Output, | |
| Inject | |
| } from "angular2/core"; | |
| import {FORM_PROVIDERS} from "angular2/common"; | |
| import {ObservableWrapper, EventEmitter, TimerWrapper} from "angular2/src/facade/async"; | |
| import {isBlank} from "angular2/src/facade/lang"; | |
| import {DOM} from "angular2/src/platform/dom/dom_adapter"; | |
| // TODO(jd): <select> hasFocus/hasValue classes | |
| // TODO(jd): input container validation styles. | |
| // TODO(jelbourn): textarea resizing | |
| // TODO(jelbourn): max-length counter | |
| @Directive({ | |
| selector: "input[md-input],input.md-input,textarea[md-input],textarea.md-input", | |
| host: { | |
| "class": "md-input", | |
| "[value]": "value", | |
| "(input)": "value=$event.target.value", | |
| "(focus)": "setHasFocus(true)", | |
| "(blur)": "setHasFocus(false)" | |
| }, | |
| providers: [FORM_PROVIDERS] | |
| }) | |
| export class MdInputWrapper implements OnChanges { | |
| @Input("value") | |
| _value:string; | |
| @Input() | |
| placeholder:string; | |
| @Output() | |
| mdChange:EventEmitter<any> = new EventEmitter(); | |
| @Output() | |
| mdFocusChange:EventEmitter<any> = new EventEmitter(); | |
| set value(value:string) { | |
| this._value = value; | |
| ObservableWrapper.callEmit(this.mdChange, this.value); | |
| } | |
| get value():string { | |
| return !isBlank(this._value) ? this._value : ""; | |
| } | |
| setHasFocus(hasFocus:boolean) { | |
| ObservableWrapper.callEmit(this.mdFocusChange, hasFocus); | |
| } | |
| ngOnChanges(changes) { | |
| let valueChange = changes._value; | |
| if (valueChange && !valueChange.previousValue) { | |
| this.value = valueChange.currentValue; //touch to trigger set value emit | |
| } | |
| } | |
| } | |
| @Component({ | |
| selector: "md-input-container", | |
| host: { | |
| "[class.md-input-has-value]": "inputHasValue", | |
| "[class.md-input-has-placeholder]": "inputHasPlaceholder", | |
| "[class.md-input-focused]": "inputHasFocus", | |
| } | |
| }) | |
| @View({ | |
| template: `<ng-content></ng-content><div class="md-errors-spacer"></div>` | |
| }) | |
| export class MdInputContainer implements AfterContentInit, OnChanges { | |
| // The MdInput or MdTextarea inside of this container. | |
| @ContentChild(MdInputWrapper) | |
| _input:MdInputWrapper = null; | |
| // Whether the input inside of this container has a non-empty value. | |
| inputHasValue:boolean = false; | |
| // Whether the input inside of this container has focus. | |
| inputHasFocus:boolean = false; | |
| // Whether the input inside of this container has a placeholder | |
| inputHasPlaceholder:boolean = false; | |
| _element; | |
| constructor(@Inject(ElementRef)_element:ElementRef) { | |
| this._element = _element; | |
| } | |
| ngOnChanges(_) { | |
| this.inputHasValue = this._input.value !== ""; | |
| // TODO(jd): Is there something like @ContentChild that accepts a selector? I would prefer not to | |
| // use a directive for label elements because I cannot use a parent->child selector to make them | |
| // specific to md-input | |
| this.inputHasPlaceholder = !!DOM.querySelector(this._element.nativeElement, "label") && !!this._input.placeholder; | |
| } | |
| ngAfterContentInit() { | |
| // If there is no text input, just bail and do nothing. | |
| if (this._input === null) { | |
| return; | |
| } | |
| // TODO(jd): :sob: what is the correct way to update these variables after the component initializes? | |
| // any time I do it directly here, debug mode complains about values changing after being checked. I | |
| // need to wait until the content has been initialized so that `_input` is there | |
| // For now, just wrap it in a setTimeout to let the change detection finish up, and then set the values... | |
| TimerWrapper.setTimeout(() => this.ngOnChanges({}), 0); | |
| // Listen to input changes and focus events so that we can apply the appropriate CSS | |
| // classes based on the input state. | |
| ObservableWrapper.subscribe(this._input.mdChange, (value) => { | |
| this.inputHasValue = value !== ""; | |
| }); | |
| ObservableWrapper.subscribe<boolean>(this._input.mdFocusChange, (hasFocus:boolean) => { | |
| this.inputHasFocus = hasFocus; | |
| }); | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment