Skip to content

Commit

Permalink
fix(input): continue checking for input child after initialization
Browse files Browse the repository at this point in the history
Currently we only check if an `md-input-container` has a child upon initialization, however the child might be removed via `ngIf` at a later point. If that happens, we eventually run into some check that assumed that we have an input child, however it would be better if we threw the appropriate error. These changes add extra validation that ensure that the input continues to be in place after init.

Fixes angular#4551.
  • Loading branch information
crisbeto committed May 16, 2017
1 parent 05dbb90 commit 021b62d
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 19 deletions.
24 changes: 24 additions & 0 deletions src/lib/input/input-container.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ describe('MdInputContainer', function () {
MdInputContainerWithValueBinding,
MdInputContainerZeroTestController,
MdTextareaWithBindings,
MdInputContainerWithNgIf,
],
});

Expand Down Expand Up @@ -266,6 +267,18 @@ describe('MdInputContainer', function () {
wrappedErrorMessage(getMdInputContainerMissingMdInputError()));
});

it('validates that mdInput child is present after initialization', async(() => {
let fixture = TestBed.createComponent(MdInputContainerWithNgIf);

expect(() => fixture.detectChanges()).not.toThrowError(
wrappedErrorMessage(getMdInputContainerMissingMdInputError()));

fixture.componentInstance.renderInput = false;

expect(() => fixture.detectChanges()).toThrowError(
wrappedErrorMessage(getMdInputContainerMissingMdInputError()));
}));

it('validates the type', () => {
let fixture = TestBed.createComponent(MdInputContainerInvalidTypeTestController);

Expand Down Expand Up @@ -997,3 +1010,14 @@ class MdInputContainerWithFormGroupErrorMessages {
`
})
class MdInputContainerWithPrefixAndSuffix {}

@Component({
template: `
<md-input-container>
<input mdInput *ngIf="renderInput">
</md-input-container>
`
})
class MdInputContainerWithNgIf {
renderInput = true;
}
51 changes: 32 additions & 19 deletions src/lib/input/input-container.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {
AfterContentInit,
AfterContentChecked,
AfterViewInit,
ChangeDetectorRef,
Component,
Expand Down Expand Up @@ -286,7 +287,7 @@ export class MdInputDirective {
},
encapsulation: ViewEncapsulation.None,
})
export class MdInputContainer implements AfterViewInit, AfterContentInit {
export class MdInputContainer implements AfterViewInit, AfterContentInit, AfterContentChecked {
/** Alignment of the input container's content. */
@Input() align: 'start' | 'end' = 'start';

Expand Down Expand Up @@ -356,10 +357,7 @@ export class MdInputContainer implements AfterViewInit, AfterContentInit {
@Optional() private _parentFormGroup: FormGroupDirective) { }

ngAfterContentInit() {
if (!this._mdInputChild) {
throw getMdInputContainerMissingMdInputError();
}

this._validateInputChild();
this._processHints();
this._validatePlaceholders();

Expand All @@ -368,6 +366,10 @@ export class MdInputContainer implements AfterViewInit, AfterContentInit {
this._mdInputChild._placeholderChange.subscribe(() => this._validatePlaceholders());
}

ngAfterContentChecked() {
this._validateInputChild();
}

ngAfterViewInit() {
// Avoid animations on load.
this._subscriptAnimationState = 'enter';
Expand Down Expand Up @@ -449,22 +451,33 @@ export class MdInputContainer implements AfterViewInit, AfterContentInit {
* of the currently-specified hints, as well as a generated id for the hint label.
*/
private _syncAriaDescribedby() {
let ids: string[] = [];
let startHint = this._hintChildren ?
this._hintChildren.find(hint => hint.align === 'start') : null;
let endHint = this._hintChildren ?
this._hintChildren.find(hint => hint.align === 'end') : null;

if (startHint) {
ids.push(startHint.id);
} else if (this._hintLabel) {
ids.push(this._hintLabelId);
if (this._mdInputChild) {
let ids: string[] = [];
let startHint = this._hintChildren ?
this._hintChildren.find(hint => hint.align === 'start') : null;
let endHint = this._hintChildren ?
this._hintChildren.find(hint => hint.align === 'end') : null;

if (startHint) {
ids.push(startHint.id);
} else if (this._hintLabel) {
ids.push(this._hintLabelId);
}

if (endHint) {
ids.push(endHint.id);
}

this._mdInputChild.ariaDescribedby = ids.join(' ');
}
}

if (endHint) {
ids.push(endHint.id);
/**
* Throws an error if the container's input child was removed.
*/
private _validateInputChild() {
if (!this._mdInputChild) {
throw getMdInputContainerMissingMdInputError();
}

this._mdInputChild.ariaDescribedby = ids.join(' ');
}
}

0 comments on commit 021b62d

Please sign in to comment.