When creating an object oriented CRUD operation, often you’ll have a case where an object will have children that are independently managed resources. Each child is simply associated to the parent, but when editing the entire object it’s likely that a better guest experience calls for one screen, rather than many. The editor shouldn’t have to care if they’re updating one or many objects at once.
If the parent is dependent on the children objects to complete their operations before the parent can complete theirs, then you’ll have to create a dependency tree. Each child would likely have other dependents, and would thusly need to wait until their dependencies are saved before continuing up the tree.
We cannot simply start with the last child process and work our way back up, this could result in orphaning the children that are in the outer-most parallel branches of the tree. We’ll need to begin from the parent, and recursively work our way down the branches until we’ve resolved each child’s process. Using the composite pattern, we can treat each child the same way as a single instance of an object, and in this case each one is an ngResource.
app.service 'resourceService', ['$resource', '$rootScope', '$q', ($resource, $rootScope, $q)->
$rootScope.resources = {}
resourceService =
newResource : (tag, url, paramDefaults = {}, actions = {}, options = {})->
if !$rootScope.resources[tag]
actions = _.extend actions,
update :
method : 'PUT'
resource = $resource(url, paramDefaults, actions, options)
$rootScope.resources[tag] = resourceService.extendResource resource
$rootScope.resources[tag]
autoQuery : (tag, url, paramDefaults = {}, actions = {}, options = {})->
resourceService.newResource(tag, url, paramDefaults, actions, options).query()
getResource : (tag)->
$rootScope.resources[tag]
convertToResource : (obj, resource)->
if Array.isArray obj
obj = (item = resourceService.convertToResource item, resource for item in obj)
else
proto = Object.create resource.prototype
obj = _.extend proto, obj
obj = resourceService.extendResource obj
obj
extendResource : (resource)->
if Array.isArray resource
resource = (r = resourceService.extendResource r for r in resource)
else
# Since JS won't do this for me, let's create a hash for each resource.
resource._hash = Math.random().toString(36).substring(7)
# Create a child for this resource
resource._children = []
resource.getResourceId = ()->
@_hash
resource.addChild = (child)->
# locate the hash and remove it if applicable
hash = child.getResourceId()
@_children = _.reject @_children, (child)-> return child.id == hash
@_children.push
id : hash
resource : child
resource.setCallbacks = (@successCallback, @errorCallback, @notifyCallback)->
resource
resource.resolve = ()->
children = @_children
promises = []
promises.push $q.when child.resource.resolve() for child in children
combined = $q.all promises
parent = @
combined.then(()-> parent._children = [])
.then(@successCallback, @errorCallback, @notifyCallback)
# return the resource
resource
resourceService
]