@@ -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
} ,
@@ -1324,7 +1330,7 @@ angular.module('schemaForm').provider('schemaForm',
1324
1330
1325
1331
var service = { } ;
1326
1332
1327
- service . merge = function ( schema , form , ignore , options , readonly ) {
1333
+ service . merge = function ( schema , form , ignore , options , readonly , asyncTemplates ) {
1328
1334
form = form || [ '*' ] ;
1329
1335
options = options || { } ;
1330
1336
@@ -1395,13 +1401,13 @@ angular.module('schemaForm').provider('schemaForm',
1395
1401
1396
1402
//if it's a type with items, merge 'em!
1397
1403
if ( obj . items ) {
1398
- obj . items = service . merge ( schema , obj . items , ignore , options , obj . readonly ) ;
1404
+ obj . items = service . merge ( schema , obj . items , ignore , options , obj . readonly , asyncTemplates ) ;
1399
1405
}
1400
1406
1401
1407
//if its has tabs, merge them also!
1402
1408
if ( obj . tabs ) {
1403
1409
angular . forEach ( obj . tabs , function ( tab ) {
1404
- tab . items = service . merge ( schema , tab . items , ignore , options , obj . readonly ) ;
1410
+ tab . items = service . merge ( schema , tab . items , ignore , options , obj . readonly , asyncTemplates ) ;
1405
1411
} ) ;
1406
1412
}
1407
1413
@@ -1410,6 +1416,13 @@ angular.module('schemaForm').provider('schemaForm',
1410
1416
if ( obj . type === 'checkbox' && angular . isUndefined ( obj . schema [ 'default' ] ) ) {
1411
1417
obj . schema [ 'default' ] = false ;
1412
1418
}
1419
+
1420
+ // Special case: template type with tempplateUrl that's needs to be loaded before rendering
1421
+ // TODO: this is not a clean solution. Maybe something cleaner can be made when $ref support
1422
+ // is introduced since we need to go async then anyway
1423
+ if ( asyncTemplates && obj . type === 'template' && ! obj . template && obj . templateUrl ) {
1424
+ asyncTemplates . push ( obj ) ;
1425
+ }
1413
1426
1414
1427
return obj ;
1415
1428
} ) ) ;
@@ -2281,28 +2294,52 @@ function(sel, sfPath, schemaForm) {
2281
2294
} ) ;
2282
2295
2283
2296
scope . appendToArray = function ( ) {
2284
-
2285
2297
var empty ;
2286
2298
2287
- // Same old add empty things to the array hack :(
2288
- if ( scope . form && scope . form . schema ) {
2289
- if ( scope . form . schema . items ) {
2290
- if ( scope . form . schema . items . type === 'object' ) {
2291
- empty = { } ;
2292
- } else if ( scope . form . schema . items . type === 'array' ) {
2293
- empty = [ ] ;
2294
- }
2295
- }
2296
- }
2297
-
2299
+ // Create and set an array if needed.
2298
2300
var model = scope . modelArray ;
2299
2301
if ( ! model ) {
2300
- // Create and set an array if needed.
2301
2302
var selection = sfPath . parse ( attrs . sfNewArray ) ;
2302
2303
model = [ ] ;
2303
2304
sel ( selection , scope , model ) ;
2304
2305
scope . modelArray = model ;
2305
2306
}
2307
+
2308
+ // Same old add empty things to the array hack :(
2309
+ if ( scope . form && scope . form . schema && scope . form . schema . items ) {
2310
+
2311
+ var items = scope . form . schema . items ;
2312
+ if ( items . type && items . type . indexOf ( 'object' ) !== - 1 ) {
2313
+ empty = { } ;
2314
+
2315
+ // Check for possible defaults
2316
+ if ( ! scope . options || scope . options . setSchemaDefaults !== false ) {
2317
+ empty = angular . isDefined ( items [ 'default' ] ) ? items [ 'default' ] : empty ;
2318
+
2319
+ // Check for defaults further down in the schema.
2320
+ // If the default instance sets the new array item to something falsy, i.e. null
2321
+ // then there is no need to go further down.
2322
+ if ( empty ) {
2323
+ schemaForm . traverseSchema ( items , function ( prop , path ) {
2324
+ if ( angular . isDefined ( prop [ 'default' ] ) ) {
2325
+ sel ( path , empty , prop [ 'default' ] ) ;
2326
+ }
2327
+ } ) ;
2328
+ }
2329
+ }
2330
+
2331
+ } else if ( items . type && items . type . indexOf ( 'array' ) !== - 1 ) {
2332
+ empty = [ ] ;
2333
+ if ( ! scope . options || scope . options . setSchemaDefaults !== false ) {
2334
+ empty = items [ 'default' ] || empty ;
2335
+ }
2336
+ } else {
2337
+ // No type? could still have defaults.
2338
+ if ( ! scope . options || scope . options . setSchemaDefaults !== false ) {
2339
+ empty = items [ 'default' ] || empty ;
2340
+ }
2341
+ }
2342
+ }
2306
2343
model . push ( empty ) ;
2307
2344
2308
2345
return model ;
@@ -2369,8 +2406,8 @@ FIXME: real documentation
2369
2406
2370
2407
angular . module ( 'schemaForm' )
2371
2408
. directive ( 'sfSchema' ,
2372
- [ '$compile' , 'schemaForm' , 'schemaFormDecorators' , 'sfSelect' , 'sfPath' , 'sfBuilder' ,
2373
- function ( $compile , schemaForm , schemaFormDecorators , sfSelect , sfPath , sfBuilder ) {
2409
+ [ '$compile' , '$http' , '$templateCache' , '$q' , ' schemaForm', 'schemaFormDecorators' , 'sfSelect' , 'sfPath' , 'sfBuilder' ,
2410
+ function ( $compile , $http , $templateCache , $q , schemaForm , schemaFormDecorators , sfSelect , sfPath , sfBuilder ) {
2374
2411
2375
2412
return {
2376
2413
scope : {
@@ -2429,8 +2466,27 @@ angular.module('schemaForm')
2429
2466
2430
2467
// Common renderer function, can either be triggered by a watch or by an event.
2431
2468
var render = function ( schema , form ) {
2432
- var merged = schemaForm . merge ( schema , form , ignore , scope . options ) ;
2469
+ var asyncTemplates = [ ] ;
2470
+ var merged = schemaForm . merge ( schema , form , ignore , scope . options , undefined , asyncTemplates ) ;
2471
+
2472
+ if ( asyncTemplates . length > 0 ) {
2473
+ // Pre load all async templates and put them on the form for the builder to use.
2474
+ $q . all ( asyncTemplates . map ( function ( form ) {
2475
+ return $http . get ( form . templateUrl , { cache : $templateCache } ) . then ( function ( res ) {
2476
+ form . template = res . data ;
2477
+ } ) ;
2478
+ } ) ) . then ( function ( ) {
2479
+ internalRender ( schema , form , merged ) ;
2480
+ } ) ;
2481
+
2482
+ } else {
2483
+ internalRender ( schema , form , merged ) ;
2484
+ }
2485
+
2486
+
2487
+ } ;
2433
2488
2489
+ var internalRender = function ( schema , form , merged ) {
2434
2490
// Create a new form and destroy the old one.
2435
2491
// Not doing keeps old form elements hanging around after
2436
2492
// they have been removed from the DOM
0 commit comments