Skip to content

Instantly share code, notes, and snippets.

@mliszcz
Created July 16, 2015 12:49
Show Gist options
  • Save mliszcz/a2f46501d45b5ae8b6e0 to your computer and use it in GitHub Desktop.
Save mliszcz/a2f46501d45b5ae8b6e0 to your computer and use it in GitHub Desktop.
app = angular.module 'tool'
app.controller 'dynamicFormsUpdateController', ($scope, $modalInstance, $q, _,
SchemaResolver, regularFormFilter, model, schema, resource, name, fields) ->
# NOTE
# angular-schema-form 0.8.3 contains bug realted to selects without trackBy
# https://github.com/Textalk/angular-schema-form/pull/418
# trackBy support was reverted in 0.8.4
# we will stick to the 0.8.3 til trackBy comes back
# TODO consider loading related entities one, in list controller
schemaCopy = angular.copy schema
props = schemaCopy.properties
# fields displayed on form (only primitive types)
simpleFields = fields.filter (k) ->
props[k].type != 'array' && props[k].type != 'object'
# do not allow to change PKs
if model.id then simpleFields = _.without(simpleFields, 'id')
if model.nsnid then simpleFields = _.without(simpleFields, 'nsnid')
# require all fields (required checkbox (boolean) must be checked!)
schema.required = simpleFields.filter (k) -> props[k].type != 'boolean'
schema.required = _.chain(schema.required).without('id').without('nsnid').value()
# transform date fields (timestamp integer) into date fields
dateFields = fields.filter (k) -> props[k].format == 'UTC_MILLISEC'
displayFields = simpleFields.map (field) ->
if props[field].format == 'UTC_MILLISEC'
props[field].type = 'string'
props[field].format = 'date'
key: field
type: 'datepicker'
format: 'yyyy-mm-dd'
else
field
# when POSTing, model shall contain only 'owned' entities
owningSideArrayFields = fields.filter (k) ->
props[k].type == 'array' && props[k].description == 'owningSide'
owningSideObjectFields = fields.filter (k) ->
props[k].type == 'object' && props[k].description == 'owningSide'
jsonStringify = (obj, fields) ->
JSON.stringify(obj, fields).replace(/\"([^(\")"]+)\":/g,"$1:")
# resolves to object field definition
loadObjectFieldDef = (field) ->
$q (resolve, reject) ->
SchemaResolver.resolve(props[field].$ref)
.then (schemaMeta) ->
schemaMeta.resource.query().$promise
.then (allObjects) ->
first = allObjects[0]
track = if first then switch
when _.has(first, 'nsnid') then 'nsnid'
when _.has(first, 'id') then 'id'
else null
# track is PK of related entities
resolve
key: field
type: 'select'
description: ''
titleMap: allObjects.map (obj) ->
entry =
value: obj
name: jsonStringify(obj, schemaMeta.stringFields)
entry[track] = obj[track] if track
entry
trackBy: track if track
.catch reject
.catch reject
# resolves to array field definition
loadArrayFieldDef = (field) ->
$q (resolve, reject) ->
SchemaResolver.resolve(props[field].items.$ref)
.then (schemaMeta) ->
schemaMeta.resource.query().$promise
.then (allObjects) ->
resolve
key: field
type: 'checkboxes'
htmlClass: 'dynamicforms-checkboxes'
items: null
description: ''
titleMap: allObjects.map (obj) ->
value: obj
name: jsonStringify(obj, schemaMeta.stringFields)
.catch reject
.catch reject
$q.all(owningSideObjectFields.map(loadObjectFieldDef))
.then (objectFields) ->
$q.all(owningSideArrayFields.map(loadArrayFieldDef))
.then (arrayFields) ->
# TODO redesign
safeModel =
if model.id or model.nsnid
resource.get
id: model.id
nsnid: model.nsnid
fields: owningSideArrayFields
else
m = {}
owningSideArrayFields.forEach (f) -> m[f] = []
owningSideObjectFields.forEach (f) -> m[f] = {}
m.$promise =
then: (f) -> f(m)
m
safeModel.$promise.then (model) ->
# exclude non-owned object fields
for own k,v of model
if props[k] && props[k].type == 'object'
if props[k].description != 'owningSide'
delete model[k]
# non-owned array fields are not served from backend
owningSideArrayFields.forEach (f) ->
# When rendering array ob objects as checkboxes, none is initially
# selected, cause
# angular-schema-form/src/directives/array.js:updateTitleMapValues
# compares object by reference.
# Inject custom comparator here.
$scope.$watch (() -> model[f]), () ->
model[f].indexOf = (elem) ->
_.findIndex this, (other) ->
switch
when elem.nsnid && elem.nsnid == other.nsnid then true
when elem.id && elem.id == other.id then true
else false
dateFields.forEach (f) ->
# Datepicker stores dates as strings, backend expects timestamps.
$scope.$watch (() -> model[f]), (val) ->
if angular.isString(val)
model[f] = (new Date(val)).getTime()
$scope.name = name
$scope.dynamic =
model: model
form: displayFields.concat(objectFields).concat(arrayFields)
schema: schemaCopy
$scope.submitForm = () ->
$scope.$broadcast 'schemaFormValidate'
if not $scope.updateForm.$valid then return
$scope.submitInProgress = true
$scope.error = false
m = $scope.dynamic.model
result = if m.id then resource.update(m) else resource.save(m)
result.$promise
.then ->
$modalInstance.close()
.catch ->
$scope.error = true
.finally ->
$scope.submitInProgress = false
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment