Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

Add property name translation support. #64

Closed
wants to merge 1 commit into from

2 participants

@jeremywiebe

This commit adds the ability to provide a 'propertyNameTranslator' function when mapping fromJS(). I needed this because my js model casing did not match my ASP.NET MVC view model casing (I didn't want my C# view models having property names that started in lowercase and vice versa).

The behavior that I wrote demonstrates the usage. I'm not sure if I got all of the places that may need this translation though. I'm not sure this is a relevant change to the knockout.mapping plugin or if it's something that anyone else would want, but I'm offering it up for now.

@jeremywiebe

This commit adds the ability to provide a 'propertyNameTranslator' function when mapping fromJS(). I needed this because my js model casing did not match my ASP.NET MVC view model casing (I didn't want my C# view models having property names that started in lowercase and vice versa).

The behavior that I wrote demonstrates the usage. I'm not sure if I got all of the places that may need this translation though.

Collaborator

That's a nice feature to have. For completeness and consistency it would also need to handle the inverse case though (e.g. calls to ko.mapping.toJS). Can you take a stab at it?

Incidentally, when doing this using ASP.NET MVC, what we have done in the past is change the settings of the JSON Serializer to handle these casing issues.

@jeremywiebe

And just after I created this I found this: http://stackoverflow.com/questions/9263210/how-to-map-one-property-to-another-with-knockout-mapping

I am actually going to go with the answer to this SO question instead. I'll leave the pull request here for reference.

@RoyJacobs
Collaborator

Good idea :) Still, thanks for the pull request. I'm sure it can come in useful for someone in the future.

@RoyJacobs RoyJacobs closed this
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Apr 11, 2012
  1. @jeremywiebe
This page is out of date. Refresh to see the latest.
Showing with 45 additions and 8 deletions.
  1. +21 −8 knockout.mapping.js
  2. +24 −0 spec/mappingBehaviors.js
View
29 knockout.mapping.js
@@ -381,25 +381,31 @@
// For non-atomic types, visit all properties and update recursively
visitPropertiesOrArrayEntries(rootObject, function (indexer) {
var fullPropertyName = getPropertyName(parentPropertyName, rootObject, indexer);
+ var mappedIndexer = indexer;
+
+ if (options.propertyNameTranslator) {
+ fullPropertyName = options.propertyNameTranslator(fullPropertyName);
+ mappedIndexer = options.propertyNameTranslator(indexer);
+ }
if (ko.utils.arrayIndexOf(options.ignore, fullPropertyName) != -1) {
return;
}
if (ko.utils.arrayIndexOf(options.copy, fullPropertyName) != -1) {
- mappedRootObject[indexer] = rootObject[indexer];
+ mappedRootObject[mappedIndexer] = rootObject[indexer];
return;
}
// In case we are adding an already mapped property, fill it with the previously mapped property value to prevent recursion.
// If this is a property that was generated by fromJS, we should use the options specified there
var prevMappedProperty = visitedObjects.get(rootObject[indexer]);
- var value = prevMappedProperty || updateViewModel(mappedRootObject[indexer], rootObject[indexer], options, indexer, mappedRootObject, fullPropertyName);
+ var value = prevMappedProperty || updateViewModel(mappedRootObject[mappedIndexer], rootObject[indexer], options, indexer, mappedRootObject, fullPropertyName);
- if (ko.isWriteableObservable(mappedRootObject[indexer])) {
- mappedRootObject[indexer](ko.utils.unwrapObservable(value));
+ if (ko.isWriteableObservable(mappedRootObject[mappedIndexer])) {
+ mappedRootObject[mappedIndexer](ko.utils.unwrapObservable(value));
} else {
- mappedRootObject[indexer] = value;
+ mappedRootObject[mappedIndexer] = value;
}
options.mappedProperties[fullPropertyName] = true;
@@ -629,6 +635,13 @@
var propertyValue = unwrappedRootObject[indexer];
var fullPropertyName = getPropertyName(parentName, unwrappedRootObject, indexer);
+
+ var mappedIndexer = indexer;
+
+ if (options.propertyNameTranslator) {
+ fullPropertyName = options.propertyNameTranslator(fullPropertyName);
+ mappedIndexer = options.propertyNameTranslator(indexer);
+ }
// If we don't want to explicitly copy the unmapped property...
if (ko.utils.arrayIndexOf(options.copy, indexer) === -1) {
@@ -648,10 +661,10 @@
case "array":
case "undefined":
var previouslyMappedValue = visitedObjects.get(propertyValue);
- mappedRootObject[indexer] = (exports.getType(previouslyMappedValue) !== "undefined") ? previouslyMappedValue : visitModel(propertyValue, callback, options, fullPropertyName);
+ mappedRootObject[mappedIndexer] = (exports.getType(previouslyMappedValue) !== "undefined") ? previouslyMappedValue : visitModel(propertyValue, callback, options, fullPropertyName);
break;
default:
- mappedRootObject[indexer] = callback(propertyValue, parentName);
+ mappedRootObject[mappedIndexer] = callback(propertyValue, parentName);
}
});
@@ -674,4 +687,4 @@
return (existingIndex >= 0) ? values[existingIndex] : undefined;
};
};
-}));
+}));
View
24 spec/mappingBehaviors.js
@@ -1561,3 +1561,27 @@ test('ko.mapping.visitModel on a regular object', function() {
value: 0
});
});
+
+test('ko.mapping.fromJS should translate property names', function() {
+
+ var target = {
+ id: ko.observable(),
+ title: ko.observable(),
+ items: ko.observableArray(),
+ child: {
+ a: ko.observable(),
+ b: ''
+ }
+ };
+
+ var obj = { Id: 1, Title: "Lorem ipsum", Items: [ 1, 2, 3 ], Child: { A: 'a', B: 'b'} };
+
+ ko.mapping.fromJS(obj, { propertyNameTranslator: function(name) { return name[0].toLowerCase() + name.slice(1); }}, target);
+
+ equal(obj.Id, target.id());
+ equal(obj.Title, target.title());
+ equal(obj.Items.length, target.items().length);
+ equal(obj.Child.A, target.child.a());
+ equal(obj.Child.B, target.child.b()); // Knockout promotes this to an observable!!!
+});
+
Something went wrong with that request. Please try again.