@@ -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 } ,
@@ -1324,7 +1330,7 @@ angular.module('schemaForm').provider('schemaForm',
13241330
13251331 var service = { } ;
13261332
1327- service . merge = function ( schema , form , ignore , options , readonly ) {
1333+ service . merge = function ( schema , form , ignore , options , readonly , asyncTemplates ) {
13281334 form = form || [ '*' ] ;
13291335 options = options || { } ;
13301336
@@ -1395,13 +1401,13 @@ angular.module('schemaForm').provider('schemaForm',
13951401
13961402 //if it's a type with items, merge 'em!
13971403 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 ) ;
13991405 }
14001406
14011407 //if its has tabs, merge them also!
14021408 if ( obj . tabs ) {
14031409 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 ) ;
14051411 } ) ;
14061412 }
14071413
@@ -1410,6 +1416,13 @@ angular.module('schemaForm').provider('schemaForm',
14101416 if ( obj . type === 'checkbox' && angular . isUndefined ( obj . schema [ 'default' ] ) ) {
14111417 obj . schema [ 'default' ] = false ;
14121418 }
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+ }
14131426
14141427 return obj ;
14151428 } ) ) ;
@@ -2281,28 +2294,52 @@ function(sel, sfPath, schemaForm) {
22812294 } ) ;
22822295
22832296 scope . appendToArray = function ( ) {
2284-
22852297 var empty ;
22862298
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.
22982300 var model = scope . modelArray ;
22992301 if ( ! model ) {
2300- // Create and set an array if needed.
23012302 var selection = sfPath . parse ( attrs . sfNewArray ) ;
23022303 model = [ ] ;
23032304 sel ( selection , scope , model ) ;
23042305 scope . modelArray = model ;
23052306 }
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+ }
23062343 model . push ( empty ) ;
23072344
23082345 return model ;
@@ -2369,8 +2406,8 @@ FIXME: real documentation
23692406
23702407angular . module ( 'schemaForm' )
23712408 . 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 ) {
23742411
23752412 return {
23762413 scope : {
@@ -2429,8 +2466,27 @@ angular.module('schemaForm')
24292466
24302467 // Common renderer function, can either be triggered by a watch or by an event.
24312468 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+ } ;
24332488
2489+ var internalRender = function ( schema , form , merged ) {
24342490 // Create a new form and destroy the old one.
24352491 // Not doing keeps old form elements hanging around after
24362492 // they have been removed from the DOM
0 commit comments