@@ -255,7 +255,7 @@ angular.module('schemaForm').provider('sfBuilder', ['sfPathProvider', function(s
255
255
'"modelValue": model' + ( strKey [ 0 ] === '[' ? '' : '.' ) + strKey + '})' ;
256
256
}
257
257
258
- var children = args . fieldFrag . children ;
258
+ var children = args . fieldFrag . children || args . fieldFrag . childNodes ;
259
259
for ( var i = 0 ; i < children . length ; i ++ ) {
260
260
var child = children [ i ] ;
261
261
var ngIf = child . getAttribute ( 'ng-if' ) ;
@@ -351,7 +351,7 @@ angular.module('schemaForm').provider('sfBuilder', ['sfPathProvider', function(s
351
351
// measure optmization. A good start is probably a cache of DOM nodes for a particular
352
352
// template that can be cloned instead of using innerHTML
353
353
var div = document . createElement ( 'div' ) ;
354
- var template = templateFn ( field . template ) || templateFn ( [ decorator [ 'default' ] . template ] ) ;
354
+ var template = templateFn ( f , field ) || templateFn ( f , decorator [ 'default' ] ) ;
355
355
div . innerHTML = template ;
356
356
357
357
// Move node to a document fragment, we don't want the div.
@@ -375,11 +375,14 @@ angular.module('schemaForm').provider('sfBuilder', ['sfPathProvider', function(s
375
375
376
376
} ;
377
377
378
+ // Let the form definiton override builders if it wants to.
379
+ var builderFn = f . builder || field . builder ;
380
+
378
381
// Builders are either a function or a list of functions.
379
- if ( typeof field . builder === 'function' ) {
380
- field . builder ( args ) ;
382
+ if ( typeof builderFn === 'function' ) {
383
+ builderFn ( args ) ;
381
384
} else {
382
- field . builder . forEach ( function ( fn ) { fn ( args ) ; } ) ;
385
+ builderFn . forEach ( function ( fn ) { fn ( args ) ; } ) ;
383
386
}
384
387
385
388
// Append
@@ -396,8 +399,11 @@ angular.module('schemaForm').provider('sfBuilder', ['sfPathProvider', function(s
396
399
* Builds a form from a canonical form definition
397
400
*/
398
401
build : function ( form , decorator , slots , lookup ) {
399
- return build ( form , decorator , function ( url ) {
400
- return $templateCache . get ( url ) ;
402
+ return build ( form , decorator , function ( form , field ) {
403
+ if ( form . type === 'template' ) {
404
+ return form . template ;
405
+ }
406
+ return $templateCache . get ( field . template ) ;
401
407
} , slots , undefined , undefined , lookup ) ;
402
408
403
409
} ,
@@ -1332,7 +1338,7 @@ angular.module('schemaForm').provider('schemaForm',
1332
1338
1333
1339
var service = { } ;
1334
1340
1335
- service . merge = function ( schema , form , ignore , options , readonly ) {
1341
+ service . merge = function ( schema , form , ignore , options , readonly , asyncTemplates ) {
1336
1342
form = form || [ '*' ] ;
1337
1343
options = options || { } ;
1338
1344
@@ -1403,13 +1409,13 @@ angular.module('schemaForm').provider('schemaForm',
1403
1409
1404
1410
//if it's a type with items, merge 'em!
1405
1411
if ( obj . items ) {
1406
- obj . items = service . merge ( schema , obj . items , ignore , options , obj . readonly ) ;
1412
+ obj . items = service . merge ( schema , obj . items , ignore , options , obj . readonly , asyncTemplates ) ;
1407
1413
}
1408
1414
1409
1415
//if its has tabs, merge them also!
1410
1416
if ( obj . tabs ) {
1411
1417
angular . forEach ( obj . tabs , function ( tab ) {
1412
- tab . items = service . merge ( schema , tab . items , ignore , options , obj . readonly ) ;
1418
+ tab . items = service . merge ( schema , tab . items , ignore , options , obj . readonly , asyncTemplates ) ;
1413
1419
} ) ;
1414
1420
}
1415
1421
@@ -1418,6 +1424,13 @@ angular.module('schemaForm').provider('schemaForm',
1418
1424
if ( obj . type === 'checkbox' && angular . isUndefined ( obj . schema [ 'default' ] ) ) {
1419
1425
obj . schema [ 'default' ] = false ;
1420
1426
}
1427
+
1428
+ // Special case: template type with tempplateUrl that's needs to be loaded before rendering
1429
+ // TODO: this is not a clean solution. Maybe something cleaner can be made when $ref support
1430
+ // is introduced since we need to go async then anyway
1431
+ if ( asyncTemplates && obj . type === 'template' && ! obj . template && obj . templateUrl ) {
1432
+ asyncTemplates . push ( obj ) ;
1433
+ }
1421
1434
1422
1435
return obj ;
1423
1436
} ) ) ;
@@ -2289,28 +2302,52 @@ function(sel, sfPath, schemaForm) {
2289
2302
} ) ;
2290
2303
2291
2304
scope . appendToArray = function ( ) {
2292
-
2293
2305
var empty ;
2294
2306
2295
- // Same old add empty things to the array hack :(
2296
- if ( scope . form && scope . form . schema ) {
2297
- if ( scope . form . schema . items ) {
2298
- if ( scope . form . schema . items . type === 'object' ) {
2299
- empty = { } ;
2300
- } else if ( scope . form . schema . items . type === 'array' ) {
2301
- empty = [ ] ;
2302
- }
2303
- }
2304
- }
2305
-
2307
+ // Create and set an array if needed.
2306
2308
var model = scope . modelArray ;
2307
2309
if ( ! model ) {
2308
- // Create and set an array if needed.
2309
2310
var selection = sfPath . parse ( attrs . sfNewArray ) ;
2310
2311
model = [ ] ;
2311
2312
sel ( selection , scope , model ) ;
2312
2313
scope . modelArray = model ;
2313
2314
}
2315
+
2316
+ // Same old add empty things to the array hack :(
2317
+ if ( scope . form && scope . form . schema && scope . form . schema . items ) {
2318
+
2319
+ var items = scope . form . schema . items ;
2320
+ if ( items . type && items . type . indexOf ( 'object' ) !== - 1 ) {
2321
+ empty = { } ;
2322
+
2323
+ // Check for possible defaults
2324
+ if ( ! scope . options || scope . options . setSchemaDefaults !== false ) {
2325
+ empty = angular . isDefined ( items [ 'default' ] ) ? items [ 'default' ] : empty ;
2326
+
2327
+ // Check for defaults further down in the schema.
2328
+ // If the default instance sets the new array item to something falsy, i.e. null
2329
+ // then there is no need to go further down.
2330
+ if ( empty ) {
2331
+ schemaForm . traverseSchema ( items , function ( prop , path ) {
2332
+ if ( angular . isDefined ( prop [ 'default' ] ) ) {
2333
+ sel ( path , empty , prop [ 'default' ] ) ;
2334
+ }
2335
+ } ) ;
2336
+ }
2337
+ }
2338
+
2339
+ } else if ( items . type && items . type . indexOf ( 'array' ) !== - 1 ) {
2340
+ empty = [ ] ;
2341
+ if ( ! scope . options || scope . options . setSchemaDefaults !== false ) {
2342
+ empty = items [ 'default' ] || empty ;
2343
+ }
2344
+ } else {
2345
+ // No type? could still have defaults.
2346
+ if ( ! scope . options || scope . options . setSchemaDefaults !== false ) {
2347
+ empty = items [ 'default' ] || empty ;
2348
+ }
2349
+ }
2350
+ }
2314
2351
model . push ( empty ) ;
2315
2352
2316
2353
return model ;
@@ -2377,8 +2414,8 @@ FIXME: real documentation
2377
2414
2378
2415
angular . module ( 'schemaForm' )
2379
2416
. directive ( 'sfSchema' ,
2380
- [ '$compile' , 'schemaForm' , 'schemaFormDecorators' , 'sfSelect' , 'sfPath' , 'sfBuilder' ,
2381
- function ( $compile , schemaForm , schemaFormDecorators , sfSelect , sfPath , sfBuilder ) {
2417
+ [ '$compile' , '$http' , '$templateCache' , '$q' , ' schemaForm', 'schemaFormDecorators' , 'sfSelect' , 'sfPath' , 'sfBuilder' ,
2418
+ function ( $compile , $http , $templateCache , $q , schemaForm , schemaFormDecorators , sfSelect , sfPath , sfBuilder ) {
2382
2419
2383
2420
return {
2384
2421
scope : {
@@ -2437,8 +2474,27 @@ angular.module('schemaForm')
2437
2474
2438
2475
// Common renderer function, can either be triggered by a watch or by an event.
2439
2476
var render = function ( schema , form ) {
2440
- var merged = schemaForm . merge ( schema , form , ignore , scope . options ) ;
2477
+ var asyncTemplates = [ ] ;
2478
+ var merged = schemaForm . merge ( schema , form , ignore , scope . options , undefined , asyncTemplates ) ;
2479
+
2480
+ if ( asyncTemplates . length > 0 ) {
2481
+ // Pre load all async templates and put them on the form for the builder to use.
2482
+ $q . all ( asyncTemplates . map ( function ( form ) {
2483
+ return $http . get ( form . templateUrl , { cache : $templateCache } ) . then ( function ( res ) {
2484
+ form . template = res . data ;
2485
+ } ) ;
2486
+ } ) ) . then ( function ( ) {
2487
+ internalRender ( schema , form , merged ) ;
2488
+ } ) ;
2489
+
2490
+ } else {
2491
+ internalRender ( schema , form , merged ) ;
2492
+ }
2493
+
2494
+
2495
+ } ;
2441
2496
2497
+ var internalRender = function ( schema , form , merged ) {
2442
2498
// Create a new form and destroy the old one.
2443
2499
// Not doing keeps old form elements hanging around after
2444
2500
// they have been removed from the DOM
0 commit comments