1
1
/** @module ng1 */ /** */
2
2
import { State } from "../state/stateObject" ;
3
- import { pick , forEach } from "../common/common" ;
3
+ import { pick , forEach , anyTrueR , unnestR , kebobString } from "../common/common" ;
4
4
import { ViewConfig , ViewContext } from "../view/interface" ;
5
5
import { Ng1ViewDeclaration } from "./interface" ;
6
6
import { ViewService } from "../view/view" ;
7
- import { isInjectable } from "../common/predicates" ;
7
+ import { isInjectable , isDefined , isString , isObject } from "../common/predicates" ;
8
8
import { services } from "../common/coreservices" ;
9
9
import { trace } from "../common/trace" ;
10
10
import { Node } from "../path/node" ;
11
11
import { TemplateFactory } from "../view/templateFactory" ;
12
12
import { ResolveContext } from "../resolve/resolveContext" ;
13
+ import { prop , parse } from "../common/hof" ;
13
14
14
15
export const ng1ViewConfigFactory = ( node , view ) => new Ng1ViewConfig ( node , view ) ;
15
16
@@ -24,19 +25,36 @@ export const ng1ViewConfigFactory = (node, view) => new Ng1ViewConfig(node, view
24
25
*/
25
26
export function ng1ViewsBuilder ( state : State ) {
26
27
let tplKeys = [ 'templateProvider' , 'templateUrl' , 'template' , 'notify' , 'async' ] ,
27
- ctrlKeys = [ 'component' , 'controller' , 'controllerProvider' , 'controllerAs' , 'resolveAs' ] ,
28
- allKeys = tplKeys . concat ( ctrlKeys ) ;
28
+ ctrlKeys = [ 'controller' , 'controllerProvider' , 'controllerAs' , 'resolveAs' ] ,
29
+ compKeys = [ 'component' , 'bindings' ] ,
30
+ nonCompKeys = tplKeys . concat ( ctrlKeys ) ,
31
+ allKeys = compKeys . concat ( nonCompKeys ) ;
29
32
30
33
let views = { } , viewsObject = state . views || { "$default" : pick ( state , allKeys ) } ;
31
34
32
35
forEach ( viewsObject , function ( config : Ng1ViewDeclaration , name ) {
33
- name = name || "$default" ; // Account for views: { "": { template... } }
34
- // Allow controller settings to be defined at the state level for all views
35
- forEach ( ctrlKeys , ( key ) => {
36
- if ( state [ key ] && ! config [ key ] ) config [ key ] = state [ key ] ;
37
- } ) ;
36
+ // Account for views: { "": { template... } }
37
+ name = name || "$default" ;
38
+ // Account for views: { header: "headerComponent" }
39
+ if ( isString ( config ) ) config = { component : < string > config } ;
38
40
if ( ! Object . keys ( config ) . length ) return ;
39
41
42
+ // Configure this view for routing to an angular 1.5+ style .component (or any directive, really)
43
+ if ( config . component ) {
44
+ if ( nonCompKeys . map ( key => isDefined ( config [ key ] ) ) . reduce ( anyTrueR , false ) ) {
45
+ throw new Error ( `Cannot combine: ${ compKeys . join ( "|" ) } with: ${ nonCompKeys . join ( "|" ) } in stateview: 'name@${ state . name } '` ) ;
46
+ }
47
+
48
+ // Dynamically build a template like "<component-name input1='$resolve.foo'></component-name>"
49
+ config . templateProvider = [ '$injector' , function ( $injector ) {
50
+ const resolveFor = key => config . bindings && config . bindings [ key ] || key ;
51
+ const prefix = angular . version . minor >= 3 ? "::" : "" ;
52
+ let attrs = getComponentInputs ( $injector , config . component ) . map ( key => `${ kebobString ( key ) } ='${ prefix } $resolve.${ resolveFor ( key ) } '` ) . join ( " " ) ;
53
+ let kebobName = kebobString ( config . component ) ;
54
+ return `<${ kebobName } ${ attrs } ></${ kebobName } >` ;
55
+ } ] ;
56
+ }
57
+
40
58
config . resolveAs = config . resolveAs || '$resolve' ;
41
59
config . $type = "ng1" ;
42
60
config . $context = state ;
@@ -51,6 +69,33 @@ export function ng1ViewsBuilder(state: State) {
51
69
return views ;
52
70
}
53
71
72
+ // for ng 1.2 style, process the scope: { input: "=foo" } object
73
+ const scopeBindings = bindingsObj => Object . keys ( bindingsObj )
74
+ . map ( key => [ key , / ^ [ = < ] ( .* ) / . exec ( bindingsObj [ key ] ) ] )
75
+ . filter ( tuple => isDefined ( tuple [ 1 ] ) )
76
+ . map ( tuple => tuple [ 1 ] [ 1 ] || tuple [ 0 ] ) ;
77
+
78
+ // for ng 1.3+ bindToController or 1.5 component style, process a $$bindings object
79
+ const bindToCtrlBindings = bindingsObj => Object . keys ( bindingsObj )
80
+ . filter ( key => ! ! / [ = < ] / . exec ( bindingsObj [ key ] . mode ) )
81
+ . map ( key => bindingsObj [ key ] . attrName ) ;
82
+
83
+ // Given a directive definition, find its object input attributes
84
+ // Use different properties, depending on the type of directive (component, bindToController, normal)
85
+ const getBindings = def => {
86
+ if ( isObject ( def . bindToController ) ) return scopeBindings ( def . bindToController ) ;
87
+ if ( def . $$bindings && def . $$bindings . bindToController ) return bindToCtrlBindings ( def . $$bindings . bindToController ) ;
88
+ if ( def . $$isolateBindings ) return bindToCtrlBindings ( def . $$isolateBindings ) ;
89
+ return < any > scopeBindings ( def . scope ) ;
90
+ } ;
91
+
92
+ // Gets all the directive(s)' inputs ('=' and '<')
93
+ function getComponentInputs ( $injector , name ) {
94
+ let cmpDefs = $injector . get ( name + "Directive" ) ; // could be multiple
95
+ if ( ! cmpDefs || ! cmpDefs . length ) throw new Error ( `Unable to find component named '${ name } '` ) ;
96
+ return cmpDefs . map ( getBindings ) . reduce ( unnestR , [ ] ) ;
97
+ }
98
+
54
99
export class Ng1ViewConfig implements ViewConfig {
55
100
loaded : boolean = false ;
56
101
controller : Function ;
0 commit comments