@@ -255,7 +255,7 @@ angular.module('schemaForm').provider('sfBuilder', ['sfPathProvider', function(s
255255 '"modelValue": model' + ( strKey [ 0 ] === '[' ? '' : '.' ) + strKey + '})' ;
256256 }
257257
258- var children = args . fieldFrag . children ;
258+ var children = args . fieldFrag . children || args . fieldFrag . childNodes ;
259259 for ( var i = 0 ; i < children . length ; i ++ ) {
260260 var child = children [ i ] ;
261261 var ngIf = child . getAttribute ( 'ng-if' ) ;
@@ -351,7 +351,7 @@ angular.module('schemaForm').provider('sfBuilder', ['sfPathProvider', function(s
351351 // measure optmization. A good start is probably a cache of DOM nodes for a particular
352352 // template that can be cloned instead of using innerHTML
353353 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' ] ) ;
355355 div . innerHTML = template ;
356356
357357 // Move node to a document fragment, we don't want the div.
@@ -375,11 +375,14 @@ angular.module('schemaForm').provider('sfBuilder', ['sfPathProvider', function(s
375375
376376 } ;
377377
378+ // Let the form definiton override builders if it wants to.
379+ var builderFn = f . builder || field . builder ;
380+
378381 // 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 ) ;
381384 } else {
382- field . builder . forEach ( function ( fn ) { fn ( args ) ; } ) ;
385+ builderFn . forEach ( function ( fn ) { fn ( args ) ; } ) ;
383386 }
384387
385388 // Append
@@ -396,8 +399,11 @@ angular.module('schemaForm').provider('sfBuilder', ['sfPathProvider', function(s
396399 * Builds a form from a canonical form definition
397400 */
398401 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 ) ;
401407 } , slots , undefined , undefined , lookup ) ;
402408
403409 } ,
@@ -1332,7 +1338,7 @@ angular.module('schemaForm').provider('schemaForm',
13321338
13331339 var service = { } ;
13341340
1335- service . merge = function ( schema , form , ignore , options , readonly ) {
1341+ service . merge = function ( schema , form , ignore , options , readonly , asyncTemplates ) {
13361342 form = form || [ '*' ] ;
13371343 options = options || { } ;
13381344
@@ -1403,13 +1409,13 @@ angular.module('schemaForm').provider('schemaForm',
14031409
14041410 //if it's a type with items, merge 'em!
14051411 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 ) ;
14071413 }
14081414
14091415 //if its has tabs, merge them also!
14101416 if ( obj . tabs ) {
14111417 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 ) ;
14131419 } ) ;
14141420 }
14151421
@@ -1418,6 +1424,13 @@ angular.module('schemaForm').provider('schemaForm',
14181424 if ( obj . type === 'checkbox' && angular . isUndefined ( obj . schema [ 'default' ] ) ) {
14191425 obj . schema [ 'default' ] = false ;
14201426 }
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+ }
14211434
14221435 return obj ;
14231436 } ) ) ;
@@ -2289,28 +2302,52 @@ function(sel, sfPath, schemaForm) {
22892302 } ) ;
22902303
22912304 scope . appendToArray = function ( ) {
2292-
22932305 var empty ;
22942306
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.
23062308 var model = scope . modelArray ;
23072309 if ( ! model ) {
2308- // Create and set an array if needed.
23092310 var selection = sfPath . parse ( attrs . sfNewArray ) ;
23102311 model = [ ] ;
23112312 sel ( selection , scope , model ) ;
23122313 scope . modelArray = model ;
23132314 }
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+ }
23142351 model . push ( empty ) ;
23152352
23162353 return model ;
@@ -2377,8 +2414,8 @@ FIXME: real documentation
23772414
23782415angular . module ( 'schemaForm' )
23792416 . 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 ) {
23822419
23832420 return {
23842421 scope : {
@@ -2437,8 +2474,27 @@ angular.module('schemaForm')
24372474
24382475 // Common renderer function, can either be triggered by a watch or by an event.
24392476 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+ } ;
24412496
2497+ var internalRender = function ( schema , form , merged ) {
24422498 // Create a new form and destroy the old one.
24432499 // Not doing keeps old form elements hanging around after
24442500 // they have been removed from the DOM
0 commit comments