11/** @module ng1 */ /** */
22import { State } from "../state/stateObject" ;
3- import { pick , forEach } from "../common/common" ;
3+ import { pick , forEach , anyTrueR , unnestR , kebobString } from "../common/common" ;
44import { ViewConfig , ViewContext } from "../view/interface" ;
55import { Ng1ViewDeclaration } from "./interface" ;
66import { ViewService } from "../view/view" ;
7- import { isInjectable } from "../common/predicates" ;
7+ import { isInjectable , isDefined , isString , isObject } from "../common/predicates" ;
88import { services } from "../common/coreservices" ;
99import { trace } from "../common/trace" ;
1010import { Node } from "../path/node" ;
1111import { TemplateFactory } from "../view/templateFactory" ;
1212import { ResolveContext } from "../resolve/resolveContext" ;
13+ import { prop , parse } from "../common/hof" ;
1314
1415export const ng1ViewConfigFactory = ( node , view ) => new Ng1ViewConfig ( node , view ) ;
1516
@@ -24,19 +25,36 @@ export const ng1ViewConfigFactory = (node, view) => new Ng1ViewConfig(node, view
2425 */
2526export function ng1ViewsBuilder ( state : State ) {
2627 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 ) ;
2932
3033 let views = { } , viewsObject = state . views || { "$default" : pick ( state , allKeys ) } ;
3134
3235 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 } ;
3840 if ( ! Object . keys ( config ) . length ) return ;
3941
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+
4058 config . resolveAs = config . resolveAs || '$resolve' ;
4159 config . $type = "ng1" ;
4260 config . $context = state ;
@@ -51,6 +69,33 @@ export function ng1ViewsBuilder(state: State) {
5169 return views ;
5270}
5371
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+
5499export class Ng1ViewConfig implements ViewConfig {
55100 loaded : boolean = false ;
56101 controller : Function ;
0 commit comments