-
Notifications
You must be signed in to change notification settings - Fork 36
Advanced: Chaining
Chaining, also often referred to as 'Fluent API functionality', is a technique where each function returns (a flavor) itself to enable method chaining using only dots between chained statements. AutoMapperTS provides chaining support in a couple of situations.
The return type of the createMap function is the IAutoMapperCreateMapChainingFunctions interface, offering public functions displayed below. Key fact in this scenario is that each function returns the same interface to enable chaining.
forMember: (sourceProperty: string, valueOrFunction: any) => IAutoMapperCreateMapChainingFunctions;
forSourceMember: (sourceProperty: string, sourceMemberConfigurationFunction: (opts: ISourceMemberConfigurationOptions) => void) => IAutoMapperCreateMapChainingFunctions;
forAllMembers: (func: (destinationObject: any, destinationPropertyName: string, value: any) => void) => IAutoMapperCreateMapChainingFunctions;
convertToType: (typeClass: new () => any) => IAutoMapperCreateMapChainingFunctions;
convertUsing: (typeConverterClassOrFunction: any) => void;
withProfile: (profileName: string) => void;
Please notice that the convertUsing and withProfile functions do not return the IAutoMapperCreateMapChainingFunctions interface (they are void, instead) and as a result of that will stop the chain (by purpose).
Find out more about each function by following its link:
automapper
.createMap(sourceKey, destinationKey)
.forMember('ageOnId', (opts: AutoMapperJs.IMemberConfigurationOptions) =>opts.ignore())
.forMember('age', (opts: AutoMapperJs.IMemberConfigurationOptions) =>opts.mapFrom('proclaimedAge'))
.convertToType(BeerBuyingYoungster)
.withProfile('ValidatedAgeMappingProfile');
When chaining multiple functions together, it is of utmost importance the end result of chaining functions together stays a stable, understandable result. For instance, if we stack multiple forMember functions together for the same destination property, we are obviously expecting a multi-step missile (why else would we chose to go there otherwise?). Let's make clear what we intend, using a sample (one of the actual unit test bodies):
// arrange
var birthdayString = '2000-01-01T00:00:00.000Z';
var objA = { birthdayString: birthdayString };
var fromKey = 'FromBirthdayString';
var toKey = 'ToBirthdayDateObject';
automapper
.createMap(fromKey, toKey)
.forMember('birthday', (opts: AutoMapperJs.IMemberConfigurationOptions) => { opts.mapFrom('birthdayString'); })
.forMember('birthday', (opts: AutoMapperJs.IMemberConfigurationOptions) => { return new Date(opts.destinationPropertyValue); });
// act
var objB = automapper.map(fromKey, toKey, objA);
// assert
expect(objB.birthday instanceof Date).toBeTruthy();
expect(objB.birthday.toISOString()).toEqual('2000-01-01T00:00:00.000Z');
In this sample, the first step is to get the initial mapping value from the source object property birtdayString
. Since it is a string, we do not want to set the resulting value directly into the destination object. Therefore, we chain another forMember call to return the Date object for this string value.
In case you are wondering about the execution order of chains: chained methods are executed in a top-down manner. However, there are some specialties which are treated differently when discovered at configuration time. Amongst those specialties are opts.ignore() and opts.mapFrom(), which are called before any other 'normal' function call.
AutoMapperTS is Copyright © 2015 Bert Loedeman and other contributors under the MIT license.
Getting started
Mapping performance
Initialization (initialize)
Mapping configuration (createMap)
- forMember
- forSourceMember
- condition
- forAllMembers
- ignoreAllNonExisting
- convertToType
- convertUsing
- withProfile
Validation (assertConfigurationIsValid)
Mapping (map)
Currying
Custom type converters
Profiles
Chaining
Naming conventions
Asynchronous mapping
Flattening and nesting