Skip to content

Commit ba8204a

Browse files
committed
feat(ngModel): expose function to run model -> view pipeline
Closes angular#3407 Closes angular#10764
1 parent 5346772 commit ba8204a

File tree

2 files changed

+75
-18
lines changed

2 files changed

+75
-18
lines changed

src/ng/directive/ngModel.js

+22-18
Original file line numberDiff line numberDiff line change
@@ -878,6 +878,27 @@ NgModelController.prototype = {
878878
*/
879879
$overrideModelOptions: function(options) {
880880
this.$options = this.$options.createChild(options);
881+
},
882+
883+
$setModelValue: function(modelValue) {
884+
this.$modelValue = this.$$rawModelValue = modelValue;
885+
this.$$parserValid = undefined;
886+
887+
var formatters = this.$formatters,
888+
idx = formatters.length;
889+
890+
var viewValue = modelValue;
891+
while (idx--) {
892+
viewValue = formatters[idx](viewValue);
893+
}
894+
if (this.$viewValue !== viewValue) {
895+
this.$$updateEmptyClasses(viewValue);
896+
this.$viewValue = this.$$lastCommittedViewValue = viewValue;
897+
this.$render();
898+
899+
// It is possible that model and view value have been updated during render
900+
this.$$runValidators(this.$modelValue, this.$viewValue, noop);
901+
}
881902
}
882903
};
883904

@@ -903,24 +924,7 @@ function setupModelWatcher(ctrl) {
903924
// eslint-disable-next-line no-self-compare
904925
(ctrl.$modelValue === ctrl.$modelValue || modelValue === modelValue)
905926
) {
906-
ctrl.$modelValue = ctrl.$$rawModelValue = modelValue;
907-
ctrl.$$parserValid = undefined;
908-
909-
var formatters = ctrl.$formatters,
910-
idx = formatters.length;
911-
912-
var viewValue = modelValue;
913-
while (idx--) {
914-
viewValue = formatters[idx](viewValue);
915-
}
916-
if (ctrl.$viewValue !== viewValue) {
917-
ctrl.$$updateEmptyClasses(viewValue);
918-
ctrl.$viewValue = ctrl.$$lastCommittedViewValue = viewValue;
919-
ctrl.$render();
920-
921-
// It is possible that model and view value have been updated during render
922-
ctrl.$$runValidators(ctrl.$modelValue, ctrl.$viewValue, noop);
923-
}
927+
ctrl.$setModelValue(modelValue);
924928
}
925929

926930
return modelValue;

test/ng/directive/ngModelSpec.js

+53
Original file line numberDiff line numberDiff line change
@@ -603,6 +603,59 @@ describe('ngModel', function() {
603603
expect(ctrl.$modelValue).toBeNaN();
604604

605605
}));
606+
607+
describe('$setModelValue', function() {
608+
it('should run the model -> view pipeline', function() {
609+
var log = [];
610+
611+
ctrl.$formatters.unshift(function(value) {
612+
log.push(value);
613+
return value + 2;
614+
});
615+
616+
ctrl.$formatters.unshift(function(value) {
617+
log.push(value);
618+
return value + '';
619+
});
620+
621+
spyOn(ctrl, '$render');
622+
623+
ctrl.$setModelValue(3);
624+
625+
expect(ctrl.$modelValue).toBe(3);
626+
expect(log).toEqual([3, 5]);
627+
expect(ctrl.$viewValue).toBe('5');
628+
expect(ctrl.$render).toHaveBeenCalledOnce();
629+
});
630+
631+
it('should run the model -> view pipeline even if the value has not changed', function() {
632+
// this is analogue to $setViewValue
633+
spyOn(ctrl, '$render');
634+
635+
ctrl.$setModelValue(3);
636+
637+
expect(ctrl.$modelValue).toBe(3);
638+
expect(ctrl.$render).toHaveBeenCalledOnce();
639+
640+
ctrl.$setModelValue(3);
641+
expect(ctrl.$modelValue).toBe(3);
642+
expect(ctrl.$render).toHaveBeenCalledOnce();
643+
});
644+
645+
it('should not modify the scope value', function() {
646+
// this is analogue to $setViewValue, which does not modify the rendered (DOM) value
647+
scope.$apply('value = 10');
648+
expect(ctrl.$modelValue).toBe(10);
649+
expect(ctrl.$viewValue).toBe(10);
650+
651+
ctrl.$setModelValue(3);
652+
653+
expect(scope.value).toBe(10);
654+
expect(ctrl.$modelValue).toBe(3);
655+
expect(ctrl.$viewValue).toBe(3);
656+
});
657+
658+
});
606659
});
607660

608661

0 commit comments

Comments
 (0)