2016-06-23 09:47:54 -07:00
|
|
|
/**
|
|
|
|
* @license
|
|
|
|
* Copyright Google Inc. All Rights Reserved.
|
|
|
|
*
|
|
|
|
* Use of this source code is governed by an MIT-style license that can be
|
|
|
|
* found in the LICENSE file at https://angular.io/license
|
|
|
|
*/
|
|
|
|
|
2016-05-31 15:22:59 -07:00
|
|
|
import {ListWrapper, StringMapWrapper} from '../../facade/collection';
|
|
|
|
import {BaseException} from '../../facade/exceptions';
|
2016-06-08 16:38:52 -07:00
|
|
|
import {hasConstructor, isBlank, isPresent, looseIdentical} from '../../facade/lang';
|
2015-10-27 14:36:13 -07:00
|
|
|
import {Control, ControlGroup} from '../model';
|
2015-05-30 11:56:00 -07:00
|
|
|
import {Validators} from '../validators';
|
2016-06-08 16:38:52 -07:00
|
|
|
|
|
|
|
import {AbstractControlDirective} from './abstract_control_directive';
|
|
|
|
import {CheckboxControlValueAccessor} from './checkbox_value_accessor';
|
|
|
|
import {ControlContainer} from './control_container';
|
2015-09-30 17:52:33 -07:00
|
|
|
import {ControlValueAccessor} from './control_value_accessor';
|
|
|
|
import {DefaultValueAccessor} from './default_value_accessor';
|
2016-06-08 16:38:52 -07:00
|
|
|
import {NgControl} from './ng_control';
|
|
|
|
import {NgControlGroup} from './ng_control_group';
|
|
|
|
import {normalizeAsyncValidator, normalizeValidator} from './normalize_validator';
|
2015-10-15 11:38:34 -07:00
|
|
|
import {NumberValueAccessor} from './number_value_accessor';
|
2016-02-09 15:28:08 -08:00
|
|
|
import {RadioControlValueAccessor} from './radio_control_value_accessor';
|
2016-06-08 16:38:52 -07:00
|
|
|
import {SelectControlValueAccessor} from './select_control_value_accessor';
|
2016-06-27 00:24:27 +02:00
|
|
|
import {SelectMultipleControlValueAccessor} from './select_multiple_control_value_accessor';
|
2016-06-08 16:38:52 -07:00
|
|
|
import {AsyncValidatorFn, ValidatorFn} from './validators';
|
2015-06-13 12:15:42 -07:00
|
|
|
|
2015-05-30 11:56:00 -07:00
|
|
|
|
2015-06-26 11:10:52 -07:00
|
|
|
export function controlPath(name: string, parent: ControlContainer): string[] {
|
2015-05-30 11:56:00 -07:00
|
|
|
var p = ListWrapper.clone(parent.path);
|
2015-06-17 11:17:21 -07:00
|
|
|
p.push(name);
|
2015-05-30 11:56:00 -07:00
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
2015-09-01 20:59:43 -07:00
|
|
|
export function setUpControl(control: Control, dir: NgControl): void {
|
2016-06-08 16:38:52 -07:00
|
|
|
if (isBlank(control)) _throwError(dir, 'Cannot find control');
|
|
|
|
if (isBlank(dir.valueAccessor)) _throwError(dir, 'No value accessor for');
|
2015-05-30 11:56:00 -07:00
|
|
|
|
2015-09-01 20:59:43 -07:00
|
|
|
control.validator = Validators.compose([control.validator, dir.validator]);
|
2015-11-02 10:00:42 -08:00
|
|
|
control.asyncValidator = Validators.composeAsync([control.asyncValidator, dir.asyncValidator]);
|
2015-09-01 20:59:43 -07:00
|
|
|
dir.valueAccessor.writeValue(control.value);
|
2015-06-01 10:41:50 -07:00
|
|
|
|
|
|
|
// view -> model
|
2016-02-11 17:01:17 -08:00
|
|
|
dir.valueAccessor.registerOnChange((newValue: any) => {
|
2015-05-31 12:24:34 -07:00
|
|
|
dir.viewToModelUpdate(newValue);
|
2015-09-01 20:59:43 -07:00
|
|
|
control.updateValue(newValue, {emitModelToViewChange: false});
|
|
|
|
control.markAsDirty();
|
2015-05-31 12:24:34 -07:00
|
|
|
});
|
2015-06-01 10:41:50 -07:00
|
|
|
|
|
|
|
// model -> view
|
2016-02-11 17:01:17 -08:00
|
|
|
control.registerOnChange((newValue: any) => dir.valueAccessor.writeValue(newValue));
|
2015-06-02 08:41:33 -07:00
|
|
|
|
|
|
|
// touched
|
2015-09-01 20:59:43 -07:00
|
|
|
dir.valueAccessor.registerOnTouched(() => control.markAsTouched());
|
2015-05-30 11:56:00 -07:00
|
|
|
}
|
|
|
|
|
2015-10-27 14:36:13 -07:00
|
|
|
export function setUpControlGroup(control: ControlGroup, dir: NgControlGroup) {
|
2016-06-08 16:38:52 -07:00
|
|
|
if (isBlank(control)) _throwError(dir, 'Cannot find control');
|
2015-10-27 14:36:13 -07:00
|
|
|
control.validator = Validators.compose([control.validator, dir.validator]);
|
2015-11-02 10:00:42 -08:00
|
|
|
control.asyncValidator = Validators.composeAsync([control.asyncValidator, dir.asyncValidator]);
|
2015-10-27 14:36:13 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
function _throwError(dir: AbstractControlDirective, message: string): void {
|
2016-06-08 16:38:52 -07:00
|
|
|
var path = dir.path.join(' -> ');
|
2015-05-30 11:56:00 -07:00
|
|
|
throw new BaseException(`${message} '${path}'`);
|
2015-06-13 12:15:42 -07:00
|
|
|
}
|
|
|
|
|
2016-02-19 11:49:31 -08:00
|
|
|
export function composeValidators(validators: /* Array<Validator|Function> */ any[]): ValidatorFn {
|
2015-11-02 10:00:42 -08:00
|
|
|
return isPresent(validators) ? Validators.compose(validators.map(normalizeValidator)) : null;
|
|
|
|
}
|
|
|
|
|
2016-06-08 16:38:52 -07:00
|
|
|
export function composeAsyncValidators(validators: /* Array<Validator|Function> */ any[]):
|
|
|
|
AsyncValidatorFn {
|
2016-02-19 11:49:31 -08:00
|
|
|
return isPresent(validators) ? Validators.composeAsync(validators.map(normalizeAsyncValidator)) :
|
|
|
|
null;
|
2015-10-28 16:54:27 -07:00
|
|
|
}
|
|
|
|
|
2015-10-02 16:47:54 -07:00
|
|
|
export function isPropertyUpdated(changes: {[key: string]: any}, viewModel: any): boolean {
|
2016-06-08 16:38:52 -07:00
|
|
|
if (!StringMapWrapper.contains(changes, 'model')) return false;
|
|
|
|
var change = changes['model'];
|
2015-07-15 18:16:50 -07:00
|
|
|
|
|
|
|
if (change.isFirstChange()) return true;
|
|
|
|
return !looseIdentical(viewModel, change.currentValue);
|
2015-09-01 20:59:43 -07:00
|
|
|
}
|
2015-09-30 17:52:33 -07:00
|
|
|
|
|
|
|
// TODO: vsavkin remove it once https://github.com/angular/angular/issues/3011 is implemented
|
2016-06-08 16:38:52 -07:00
|
|
|
export function selectValueAccessor(
|
|
|
|
dir: NgControl, valueAccessors: ControlValueAccessor[]): ControlValueAccessor {
|
2015-09-30 17:52:33 -07:00
|
|
|
if (isBlank(valueAccessors)) return null;
|
|
|
|
|
2016-02-11 17:01:17 -08:00
|
|
|
var defaultAccessor: ControlValueAccessor;
|
|
|
|
var builtinAccessor: ControlValueAccessor;
|
|
|
|
var customAccessor: ControlValueAccessor;
|
|
|
|
valueAccessors.forEach((v: ControlValueAccessor) => {
|
2016-02-09 17:46:38 -08:00
|
|
|
if (hasConstructor(v, DefaultValueAccessor)) {
|
2015-09-30 17:52:33 -07:00
|
|
|
defaultAccessor = v;
|
|
|
|
|
2016-06-08 16:38:52 -07:00
|
|
|
} else if (
|
|
|
|
hasConstructor(v, CheckboxControlValueAccessor) || hasConstructor(v, NumberValueAccessor) ||
|
|
|
|
hasConstructor(v, SelectControlValueAccessor) ||
|
2016-06-27 00:24:27 +02:00
|
|
|
hasConstructor(v, SelectMultipleControlValueAccessor) ||
|
2016-06-08 16:38:52 -07:00
|
|
|
hasConstructor(v, RadioControlValueAccessor)) {
|
2015-09-30 17:52:33 -07:00
|
|
|
if (isPresent(builtinAccessor))
|
2016-06-08 16:38:52 -07:00
|
|
|
_throwError(dir, 'More than one built-in value accessor matches');
|
2015-09-30 17:52:33 -07:00
|
|
|
builtinAccessor = v;
|
|
|
|
|
|
|
|
} else {
|
|
|
|
if (isPresent(customAccessor))
|
2016-06-08 16:38:52 -07:00
|
|
|
_throwError(dir, 'More than one custom value accessor matches');
|
2015-09-30 17:52:33 -07:00
|
|
|
customAccessor = v;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
if (isPresent(customAccessor)) return customAccessor;
|
|
|
|
if (isPresent(builtinAccessor)) return builtinAccessor;
|
|
|
|
if (isPresent(defaultAccessor)) return defaultAccessor;
|
|
|
|
|
2016-06-08 16:38:52 -07:00
|
|
|
_throwError(dir, 'No valid value accessor for');
|
2015-09-30 17:52:33 -07:00
|
|
|
return null;
|
2015-10-02 07:37:23 -07:00
|
|
|
}
|