Skip to content
This repository was archived by the owner on Apr 30, 2018. It is now read-only.

Commit ed79403

Browse files
author
Kent C. Dodds
committed
Adding support for formatters #368
1 parent 476f0b6 commit ed79403

File tree

4 files changed

+167
-106
lines changed

4 files changed

+167
-106
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
# 6.21.0-beta.1
2+
3+
## New Features
4+
5+
- Adding support for `formatters` [#368](/../../issues/368)
6+
17
# 6.21.0-beta.0
28

39
## New Features

src/directives/formly-field.js

Lines changed: 44 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,7 @@ function formlyField($http, $q, $compile, $templateCache, $interpolate, formlyCo
329329
stopWatchingShowError();
330330
addShowMessagesWatcher();
331331
addParsers();
332+
addFormatters();
332333
}
333334
});
334335
}
@@ -354,18 +355,31 @@ function formlyField($http, $q, $compile, $templateCache, $interpolate, formlyCo
354355
}
355356

356357
function addParsers() {
358+
setParsersOrFormatters('parsers');
359+
}
360+
361+
function addFormatters() {
362+
setParsersOrFormatters('formatters');
363+
}
364+
365+
function setParsersOrFormatters(which) {
366+
let originalThingProp = 'originalParser';
367+
if (which === 'formatters') {
368+
originalThingProp = 'originalFormatter';
369+
}
370+
357371
// init with type's parsers
358-
let parsers = getParsersFromType(type);
372+
let things = getThingsFromType(type);
359373

360-
// get optionsTypes parsers
361-
parsers = formlyUtil.extendArray(parsers, getParsersFromOptionsTypes(scope.options.optionsTypes));
374+
// get optionsTypes things
375+
things = formlyUtil.extendArray(things, getThingsFromOptionsTypes(scope.options.optionsTypes));
362376

363-
// get field's parsers
364-
parsers = formlyUtil.extendArray(parsers, scope.options.parsers);
377+
// get field's things
378+
things = formlyUtil.extendArray(things, scope.options[which]);
365379

366-
// convert parsers into formlyExpression parsers
367-
angular.forEach(parsers, (parser, index) => {
368-
parsers[index] = getFormlyExpressionParser(parser);
380+
// convert things into formlyExpression things
381+
angular.forEach(things, (thing, index) => {
382+
things[index] = getFormlyExpressionThing(thing);
369383
});
370384

371385
let ngModelCtrls = scope.fc;
@@ -374,50 +388,50 @@ function formlyField($http, $q, $compile, $templateCache, $interpolate, formlyCo
374388
}
375389

376390
angular.forEach(ngModelCtrls, ngModelCtrl => {
377-
ngModelCtrl.$parsers = ngModelCtrl.$parsers.concat(...parsers);
391+
ngModelCtrl['$' + which] = ngModelCtrl['$' + which].concat(...things);
378392
});
379393

380-
function getParsersFromType(theType) {
394+
function getThingsFromType(theType) {
381395
if (!theType) {
382396
return [];
383397
}
384398
if (angular.isString(theType)) {
385399
theType = formlyConfig.getType(theType, true, scope.options);
386400
}
387-
let typeParsers = [];
401+
let typeThings = [];
388402

389-
// get parsers from parent
403+
// get things from parent
390404
if (theType.extends) {
391-
typeParsers = formlyUtil.extendArray(typeParsers, getParsersFromType(theType.extends));
405+
typeThings = formlyUtil.extendArray(typeThings, getThingsFromType(theType.extends));
392406
}
393407

394-
// get own type's parsers
395-
typeParsers = formlyUtil.extendArray(typeParsers, getDefaultOptionsParsers(theType));
408+
// get own type's things
409+
typeThings = formlyUtil.extendArray(typeThings, getDefaultOptionsProperty(theType, which, []));
396410

397-
// get parsers from optionsTypes
398-
typeParsers = formlyUtil.extendArray(
399-
typeParsers,
400-
getParsersFromOptionsTypes(getDefaultOptionsOptionsTypes(theType))
411+
// get things from optionsTypes
412+
typeThings = formlyUtil.extendArray(
413+
typeThings,
414+
getThingsFromOptionsTypes(getDefaultOptionsOptionsTypes(theType))
401415
);
402416

403-
return typeParsers;
417+
return typeThings;
404418
}
405419

406-
function getParsersFromOptionsTypes(optionsTypes = []) {
407-
let optionsTypesParsers = [];
408-
angular.forEach(optionsTypes.reverse(), optionsTypeName => {
409-
optionsTypesParsers = formlyUtil.extendArray(optionsTypesParsers, getParsersFromType(optionsTypeName));
420+
function getThingsFromOptionsTypes(optionsTypes = []) {
421+
let optionsTypesThings = [];
422+
angular.forEach(angular.copy(arrayify(optionsTypes)).reverse(), optionsTypeName => {
423+
optionsTypesThings = formlyUtil.extendArray(optionsTypesThings, getThingsFromType(optionsTypeName));
410424
});
411-
return optionsTypesParsers;
425+
return optionsTypesThings;
412426
}
413427

414-
function getFormlyExpressionParser(parser) {
415-
formlyExpressionParserFunction.originalParser = parser;
416-
return formlyExpressionParserFunction;
428+
function getFormlyExpressionThing(thing) {
429+
formlyExpressionParserOrFormatterFunction[originalThingProp] = thing;
430+
return formlyExpressionParserOrFormatterFunction;
417431

418-
function formlyExpressionParserFunction($viewValue) {
432+
function formlyExpressionParserOrFormatterFunction($viewValue) {
419433
const $modelValue = scope.options.value();
420-
return formlyUtil.formlyEval(scope, parser, $modelValue, $viewValue);
434+
return formlyUtil.formlyEval(scope, thing, $modelValue, $viewValue);
421435
}
422436
}
423437

@@ -676,10 +690,6 @@ function formlyField($http, $q, $compile, $templateCache, $interpolate, formlyCo
676690

677691

678692
// Stateless util functions
679-
function getDefaultOptionsParsers(type) {
680-
return getDefaultOptionsProperty(type, 'parsers', []);
681-
}
682-
683693
function getDefaultOptionsOptionsTypes(type) {
684694
return getDefaultOptionsProperty(type, 'optionsTypes', []);
685695
}

src/directives/formly-field.test.js

Lines changed: 116 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -960,78 +960,9 @@ describe('formly-field', function() {
960960
});
961961

962962
describe(`parsers/formatters`, () => {
963-
describe(`everything`, () => {
964-
it(`should merge the parsers and formatters in the right order`, () => {
965-
const parent1Parser1 = sinon.spy();
966-
const parent1Parser2 = sinon.spy();
967-
const parent2Parser1 = sinon.spy();
968-
const parent2Parser2 = sinon.spy();
969-
const childParser1 = sinon.spy();
970-
const childParser2 = sinon.spy();
971-
const optionType1Parser1 = sinon.spy();
972-
const optionType1Parser2 = sinon.spy();
973-
const optionType2Parser1 = sinon.spy();
974-
const optionType2Parser2 = sinon.spy();
975-
const fieldParser1 = sinon.spy();
976-
const fieldParser2 = sinon.spy();
977-
978-
formlyConfig.setType({
979-
name: 'parent1',
980-
defaultOptions: {
981-
parsers: [parent1Parser1, parent1Parser2]
982-
}
983-
});
984-
985-
formlyConfig.setType({
986-
name: 'parent2',
987-
defaultOptions: {
988-
parsers: [parent2Parser1, parent2Parser2]
989-
}
990-
});
991-
992-
formlyConfig.setType({
993-
name: 'child',
994-
template: '<input ng-model="model[options.key]" />',
995-
extends: 'parent1', // <-- note this!
996-
defaultOptions: {
997-
parsers: [childParser1, childParser2]
998-
}
999-
});
1000-
1001-
formlyConfig.setType({
1002-
name: 'optionType1',
1003-
extends: 'parent2', // <-- note this!
1004-
defaultOptions: {
1005-
parsers: [optionType1Parser1, optionType1Parser2]
1006-
}
1007-
});
1008-
1009-
formlyConfig.setType({
1010-
name: 'optionType2',
1011-
defaultOptions: {
1012-
parsers: [optionType2Parser1, optionType2Parser2]
1013-
}
1014-
});
1015-
1016-
scope.fields = [
1017-
{
1018-
type: 'child',
1019-
optionsTypes: ['optionType1', 'optionType2'],
1020-
parsers: [fieldParser1, fieldParser2]
1021-
}
1022-
];
1023-
1024-
compileAndDigest();
1025-
const ctrl = getNgModelCtrl();
1026-
const originalParsers = ctrl.$parsers.map(parser => parser.originalParser);
1027-
expect(originalParsers).to.eql([
1028-
parent1Parser1, parent1Parser2,
1029-
childParser1, childParser2,
1030-
parent2Parser1, parent2Parser2,
1031-
optionType1Parser1, optionType1Parser2,
1032-
optionType2Parser1, optionType2Parser2,
1033-
fieldParser1, fieldParser2
1034-
]);
963+
describe(`parsers`, () => {
964+
it(`should be merged in the right order`, () => {
965+
testParsersOrFormatters('parsers');
1035966
});
1036967

1037968
it(`should handle a formlyExpression as a string`, () => {
@@ -1047,6 +978,119 @@ describe('formly-field', function() {
1047978
expect(scope.model.myKey).to.equal('hello! boo!');
1048979
});
1049980
});
981+
982+
describe(`formatters`, () => {
983+
it(`should be merged in the right order`, () => {
984+
testParsersOrFormatters('formatters');
985+
});
986+
987+
it(`should handle a formlyExpression as a string`, () => {
988+
scope.fields = [getNewField({
989+
key: 'myKey',
990+
formatters: ['$viewValue + options.data.extraThing'],
991+
data: {extraThing: ' boo!'}
992+
})];
993+
compileAndDigest();
994+
scope.model.myKey = 'hello!';
995+
scope.$digest();
996+
const ctrl = getNgModelCtrl();
997+
expect(ctrl.$formatters).to.have.length(2); // ngModel adds one
998+
expect(ctrl.$viewValue).to.equal('hello! boo!');
999+
});
1000+
});
1001+
1002+
function testParsersOrFormatters(which) {
1003+
let originalThingProp = 'originalParser';
1004+
if (which === 'formatters') {
1005+
originalThingProp = 'originalFormatter';
1006+
}
1007+
const parent1Thing1 = sinon.spy(function parent1Thing1() {
1008+
});
1009+
const parent1Thing2 = sinon.spy(function parent1Thing2() {
1010+
});
1011+
const parent2Thing1 = sinon.spy(function parent2Thing1() {
1012+
});
1013+
const parent2Thing2 = sinon.spy(function parent2Thing2() {
1014+
});
1015+
const childThing1 = sinon.spy(function childThing1() {
1016+
});
1017+
const childThing2 = sinon.spy(function childThing2() {
1018+
});
1019+
const optionType1Thing1 = sinon.spy(function optionType1Thing1() {
1020+
});
1021+
const optionType1Thing2 = sinon.spy(function optionType1Thing2() {
1022+
});
1023+
const optionType2Thing1 = sinon.spy(function optionType2Thing1() {
1024+
});
1025+
const optionType2Thing2 = sinon.spy(function optionType2Thing2() {
1026+
});
1027+
const fieldThing1 = sinon.spy(function fieldThing1() {
1028+
});
1029+
const fieldThing2 = sinon.spy(function fieldThing2() {
1030+
});
1031+
1032+
formlyConfig.setType({
1033+
name: 'parent1',
1034+
defaultOptions: {
1035+
[which]: [parent1Thing1, parent1Thing2]
1036+
}
1037+
});
1038+
1039+
formlyConfig.setType({
1040+
name: 'parent2',
1041+
defaultOptions: {
1042+
[which]: [parent2Thing1, parent2Thing2]
1043+
}
1044+
});
1045+
1046+
formlyConfig.setType({
1047+
name: 'child',
1048+
template: '<input ng-model="model[options.key]" />',
1049+
extends: 'parent1', // <-- note this!
1050+
defaultOptions: {
1051+
[which]: [childThing1, childThing2]
1052+
}
1053+
});
1054+
1055+
formlyConfig.setType({
1056+
name: 'optionType1',
1057+
extends: 'parent2', // <-- note this!
1058+
defaultOptions: {
1059+
[which]: [optionType1Thing1, optionType1Thing2]
1060+
}
1061+
});
1062+
1063+
formlyConfig.setType({
1064+
name: 'optionType2',
1065+
defaultOptions: {
1066+
[which]: [optionType2Thing1, optionType2Thing2]
1067+
}
1068+
});
1069+
1070+
scope.fields = [
1071+
{
1072+
type: 'child',
1073+
optionsTypes: ['optionType1', 'optionType2'],
1074+
[which]: [fieldThing1, fieldThing2]
1075+
}
1076+
];
1077+
1078+
compileAndDigest();
1079+
const ctrl = getNgModelCtrl();
1080+
const originalThings = ctrl['$' + which].map(thing => thing[originalThingProp]);
1081+
if (which === 'formatters') {
1082+
// all ngModelControllers have a default formatter, remove that from the originalThings for our test
1083+
originalThings.shift();
1084+
}
1085+
expect(originalThings).to.eql([
1086+
parent1Thing1, parent1Thing2,
1087+
childThing1, childThing2,
1088+
parent2Thing1, parent2Thing2,
1089+
optionType1Thing1, optionType1Thing2,
1090+
optionType2Thing1, optionType2Thing2,
1091+
fieldThing1, fieldThing2
1092+
]);
1093+
}
10501094
});
10511095

10521096
describe(`link`, () => {

src/providers/formlyApiCheck.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ const fieldOptionsApiShape = {
123123
validators: validatorChecker.optional,
124124
asyncValidators: validatorChecker.optional,
125125
parsers: apiCheck.arrayOf(formlyExpression).optional,
126+
formatters: apiCheck.arrayOf(formlyExpression).optional,
126127
noFormControl: apiCheck.bool.optional,
127128
hide: apiCheck.bool.optional,
128129
hideExpression: formlyExpression.optional,

0 commit comments

Comments
 (0)