-
-
Save xak2000/62ce767d871cf6d5e0f9 to your computer and use it in GitHub Desktop.
(function () { | |
'use strict'; | |
/** | |
* @ngdoc service | |
* @name components.factory:dateTransformer | |
* | |
* @description A helper service for use in ng-resource transform methods. | |
* Example of usage: | |
* <code> | |
* var Session = $resource('/api/sessions/:id', {id: '@id'}, { | |
* query: { | |
* method: 'GET', | |
* isArray: true, | |
* transformResponse: dateTransformer.transformResponse.toDate(['start', 'end']) | |
* }, | |
* update: { | |
* method: 'PUT', | |
* transformRequest: dateTransformer.transformRequest.toLocalIsoString(['start', 'end']) | |
* } | |
* }); | |
* </code> | |
* | |
* Call Session.query() and fields 'start' and 'end' will be instances of JS Date object in each returned | |
* Session object. | |
* Call session.$update() and server will receive JSON with 'start' and 'end' fields as strings in | |
* ISO format without timezone. | |
* | |
* Supported transformers: toDate, toLocalIsoString, toZonedIsoString. | |
* | |
* If you need to transofrm Date objects in request to ISO strings in UTC timezone (2016-01-12T13:48:05.476Z) | |
* then just do nothing. All instances of Date and moment objects supports toJSON() method which will | |
* be called automatically when serializing objects to JSON. | |
* | |
* This service uses moment.js for date parsing and format because native JS Date doesn't support | |
* dates without timezone (they always parsed as UTC+0). | |
* | |
* Also Lodash is used for object manipulations. | |
* | |
*/ | |
angular | |
.module('components') | |
.factory('dateTransformer', dateTransformerFactory); | |
function dateTransformerFactory($http, _, moment) { | |
var dateTransformer = { | |
// params (paths). See transformFn description. | |
// returns transform function to use manually in code | |
toDate: toDate, | |
toLocalIsoString: toLocalIsoString, | |
toZonedIsoString: toZonedIsoString, | |
// params (paths). See transformFn description. | |
// returns $http.defaults.transformResponse.concat(<given transformer>) | |
transformResponse: { | |
toDate: transformResponseToDate, | |
toLocalIsoString: transformResponseToLocalIsoString, | |
toZonedIsoString: transformResponseToZonedIsoString | |
}, | |
// params (paths). See transformFn description. | |
// returns [<given transformer>].concat($http.defaults.transformRequest) | |
transformRequest: { | |
toDate: transformRequestToDate, | |
toLocalIsoString: transformRequestToLocalIsoString, | |
toZonedIsoString: transformRequestToZonedIsoString | |
} | |
}; | |
return dateTransformer; | |
/** | |
* Returns transform function for corresponding paths using given transformDate function to | |
* tranform between Date <=> String. | |
* | |
* @param paths String/Array<String>/Function(clonedObject, transformDate) | |
* | |
* - Can be String with path to the field to transform. If the field is nested you can use DOT | |
* to split fields in path. | |
* | |
* <pre> | |
* Examples: | |
* 'createdAt' | |
* 'status.closedAt' | |
* 'some.deeply.nested.field' | |
* </pre> | |
* | |
* - Can be array of strings where each string is path. | |
* | |
* <pre> | |
* Example: | |
* ['createdAt', 'status.closedAt', 'some.deeply.nested.field'] | |
* </pre> | |
* | |
* - Can be function which receives two values: | |
* - Object clonedObject - copy of original object to transform fields of it. | |
* - Function(Date/String) transformDate - the last parameter of transformFn function will | |
* be passed here. | |
* | |
* <pre> | |
* Example: | |
* function (clonedObject, transformDate) { | |
* clonedObject.status.closedAt = transformDate(clonedObject.status.closedAt); | |
* } | |
* </pre> | |
* | |
* @param transformDate Function(Date/String val) function to tranform between Date <=> String | |
* <pre> | |
* Example: | |
* function (val) { // val is string | |
* return moment(val).toDate(); | |
* }) | |
* function (val) { // val is Date | |
* return moment(val).format(); | |
* }) | |
* </pre> | |
* | |
* @returns Function (Array/Object data) - function which will transform fields of data Object. Or if | |
* data is Array then it will transform fields of each object in this array. | |
* <pre> | |
* Example: | |
* var data = { | |
* id: 123, | |
* createdAt: '2016-01-12T13:48:05.476Z', | |
* status: { | |
* closedAt: '2016-02-10T12:14:15.123Z' | |
* }, | |
* anotherField: 'blabla' | |
* } | |
* var transform = transformFn(['createdAt', 'status.closedAt'], function (val) { | |
* return moment(val).toDate(); | |
* }); | |
* var transformedData = transform(data); | |
* // transformedData will be new cloned object | |
* // { | |
* // id: 123, | |
* // createdAt: instanse of Date with value of '2016-01-12T13:48:05.476Z', | |
* // status: { | |
* // closedAt: instanse of Date with value of '2016-02-10T12:14:15.123Z' | |
* // }, | |
* // anotherField: 'blabla' | |
* // } | |
* </pre> | |
*/ | |
function transformFn(paths, transformDate) { | |
return function (data) { | |
if (_.isArray(data)) { | |
var result = data.map(function (object) { | |
return transformObject(object, paths, transformDate); | |
}); | |
_.difference(_.keys(data), _.keys(result)).forEach(function (key) { | |
result[key] = data[key]; | |
}); | |
return result; | |
} else if (_.isObject(data)) { | |
return transformObject(data, paths, transformDate); | |
} | |
}; | |
} | |
function transformObject(object, paths, transformDate) { | |
var clonedObject = angular.copy(object), | |
val; | |
if (_.isFunction(paths)) { | |
paths(clonedObject, transformDate); | |
} else { | |
if (_.isString(paths)) { | |
paths = [paths]; | |
} | |
if (_.isArray(paths)) { | |
_.forEach(paths, function (path) { | |
val = _.get(clonedObject, path); | |
if (_.isDate(val) || _.isString(val)) { | |
_.set(clonedObject, path, transformDate(val)); | |
} | |
}); | |
} | |
} | |
return clonedObject; | |
} | |
function transformResponseWith(transformer) { | |
return $http.defaults.transformResponse.concat(transformer); | |
} | |
function transformRequestWith(transformer) { | |
return [transformer].concat($http.defaults.transformRequest); | |
} | |
function transformResponseToDate(paths) { | |
return transformResponseWith(toDate(paths)); | |
} | |
function transformResponseToLocalIsoString(paths) { | |
return transformResponseWith(toLocalIsoString(paths)); | |
} | |
function transformResponseToZonedIsoString(paths) { | |
return transformResponseWith(toZonedIsoString(paths)); | |
} | |
function transformRequestToDate(paths) { | |
return transformRequestWith(toDate(paths)); | |
} | |
function transformRequestToLocalIsoString(paths) { | |
return transformRequestWith(toLocalIsoString(paths)); | |
} | |
function transformRequestToZonedIsoString(paths) { | |
return transformRequestWith(toZonedIsoString(paths)); | |
} | |
/** | |
* Returns transform function for corresponding paths which will transform | |
* fields from string to Date. | |
* | |
* @param paths String/Array<String>/Function(clonedObject, transformDate) | |
* @returns Function (Array/Object data) - function which will transform fields of data Object | |
* determined by paths to Date. | |
*/ | |
function toDate(paths) { | |
return transformFn(paths, function (val) { | |
return moment(val).toDate(); | |
}); | |
} | |
/** | |
* Returns transform function for corresponding paths which will transform | |
* fields from Date/moment instances to string without timezone (2016-01-12T13:48:05.476). | |
* | |
* @param paths String/Array<String>/Function(clonedObject, transformDate) | |
* @returns Function (Array/Object data) - function which will transform fields of data Object | |
* determined by paths to string without timezone. | |
*/ | |
function toLocalIsoString(paths) { | |
return transformFn(paths, function (val) { | |
return moment(val).format('YYYY-MM-DDTHH:mm:ss.SSS'); | |
}); | |
} | |
/** | |
* Returns transform function for corresponding paths which will transform | |
* fields from Date/moment instances to string with timezone (2014-09-08T08:02:17+02:00). | |
* | |
* @param paths String/Array<String>/Function(clonedObject, transformDate) | |
* @returns Function (Array/Object data) - function which will transform fields of data Object | |
* determined by paths to string with timezone. | |
*/ | |
function toZonedIsoString(paths) { | |
return transformFn(paths, function (val) { | |
return moment(val).format(); | |
}); | |
} | |
} | |
})(); |
thanks @DSpeichert, you saved me tons of time.
I had to use your fix, too, to make it work.
Other than that it's a good solution for me.
I've updated the gist with latest version of this service.
-
Tested with Lodash 4.x (previous version is for 3.x).
-
Add much more documentation.
-
Allow to use nested paths like
status.closedAt
instead of just predicates. -
Allow to use custom function to select fields for transformation. Can be useful for transforming inner array fields.
For example, we havePost
resource and receive server responce like this:{ id: 123, comments: [ { id: 1, createdAt: '2016-01-12T13:48:05.476Z', text: 'comment1' }, { id: 2, createdAt: '2016-01-15T11:21:07.123Z', text: 'comment2' }, ] }
We do like to transform
createdAt
field of each comment of Post. So we define a function:function transformComment(post, transform) { // here `transform` will be the transform function from resource definition if (angular.isArray(post.comments)) { _.forEach(post.comments, function (comment) { comment.createdAt = transform(comment.createdAt); }); } }
And then we can use this
transformComment
function instead of field name in regulartransformResponse
definition:var Post = $resource('/api/posts/:id', {id: '@id'}, { query: { method: 'GET', isArray: true, transformResponse: dateTransformer.transformResponse.toDate(transformComment) } });
Honestly, I don't remember all the changes.
I had to change line 77 to: