1
- /** @module view */ /** for typedoc */
1
+ /** @module view */
2
+ /** for typedoc */
2
3
import { ng as angular } from "./angular" ;
4
+ import { IAugmentedJQuery } from "angular" ;
3
5
import {
4
- isArray , isDefined , isFunction , isObject , services , Obj , IInjectable , tail , kebobString , unnestR , ResolveContext , Resolvable , RawParams
6
+ isArray , isDefined , isFunction , isObject , services , Obj , IInjectable , tail , kebobString , unnestR , ResolveContext ,
7
+ Resolvable , RawParams , identity
5
8
} from "ui-router-core" ;
6
9
import { Ng1ViewDeclaration } from "./interface" ;
7
10
@@ -25,13 +28,16 @@ export class TemplateFactory {
25
28
fromConfig ( config : Ng1ViewDeclaration , params : any , context : ResolveContext ) {
26
29
const defaultTemplate = "<ui-view></ui-view>" ;
27
30
31
+ const asTemplate = ( result ) => services . $q . when ( result ) . then ( str => ( { template : str } ) ) ;
32
+ const asComponent = ( result ) => services . $q . when ( result ) . then ( str => ( { component : str } ) ) ;
33
+
28
34
return (
29
- isDefined ( config . template ) ? this . fromString ( config . template , params ) :
30
- isDefined ( config . templateUrl ) ? this . fromUrl ( config . templateUrl , params ) :
31
- isDefined ( config . templateProvider ) ? this . fromProvider ( config . templateProvider , params , context ) :
32
- isDefined ( config . component ) ? this . fromComponent ( config . component , config . bindings ) :
33
- isDefined ( config . componentProvider ) ? this . fromComponentProvider ( config . componentProvider , params , context ) :
34
- defaultTemplate
35
+ isDefined ( config . template ) ? asTemplate ( this . fromString ( config . template , params ) ) :
36
+ isDefined ( config . templateUrl ) ? asTemplate ( this . fromUrl ( config . templateUrl , params ) ) :
37
+ isDefined ( config . templateProvider ) ? asTemplate ( this . fromProvider ( config . templateProvider , params , context ) ) :
38
+ isDefined ( config . component ) ? asComponent ( config . component ) :
39
+ isDefined ( config . componentProvider ) ? asComponent ( this . fromComponentProvider ( config . componentProvider , params , context ) ) :
40
+ asTemplate ( defaultTemplate )
35
41
) ;
36
42
} ;
37
43
@@ -78,50 +84,73 @@ export class TemplateFactory {
78
84
return resolvable . get ( context ) ;
79
85
} ;
80
86
87
+ /**
88
+ * Creates a component's template by invoking an injectable provider function.
89
+ *
90
+ * @param provider Function to invoke via `locals`
91
+ * @param {Function } injectFn a function used to invoke the template provider
92
+ * @return {string } The template html as a string: "<component-name input1='::$resolve.foo'></component-name>".
93
+ */
94
+ fromComponentProvider ( provider : IInjectable , params : any , context : ResolveContext ) {
95
+ let deps = services . $injector . annotate ( provider ) ;
96
+ let providerFn = isArray ( provider ) ? tail ( < any [ ] > provider ) : provider ;
97
+ let resolvable = new Resolvable ( "" , < Function > providerFn , deps ) ;
98
+ return resolvable . get ( context ) ;
99
+ } ;
100
+
81
101
/**
82
102
* Creates a template from a component's name
83
103
*
104
+ * @param uiView {object} The parent ui-view (for binding outputs to callbacks)
105
+ * @param context The ResolveContext (for binding outputs to callbacks returned from resolves)
84
106
* @param component {string} Component's name in camel case.
85
107
* @param bindings An object defining the component's bindings: {foo: '<'}
86
108
* @return {string } The template as a string: "<component-name input1='::$resolve.foo'></component-name>".
87
109
*/
88
- fromComponent ( component : string , bindings ?: any ) {
89
- const resolveFor = ( key : string ) =>
90
- bindings && bindings [ key ] || key ;
110
+ makeComponentTemplate ( uiView : IAugmentedJQuery , context : ResolveContext , component : string , bindings ?: any ) {
111
+ bindings = bindings || { } ;
112
+
91
113
// Bind once prefix
92
114
const prefix = angular . version . minor >= 3 ? "::" : "" ;
115
+
93
116
const attributeTpl = ( input : BindingTuple ) => {
94
- var attrName = kebobString ( input . name ) ;
95
- var resolveName = resolveFor ( input . name ) ;
96
- if ( input . type === '@' )
117
+ let { name, type } = input ;
118
+ let attrName = kebobString ( name ) ;
119
+ // If the ui-view has an attribute which matches a binding on the routed component
120
+ // then pass that attribute through to the routed component template.
121
+ // Prefer ui-view wired mappings to resolve data, unless the resolve was explicitly bound using `bindings:`
122
+ if ( uiView . attr ( attrName ) && ! bindings [ name ] )
123
+ return `${ attrName } ='${ uiView . attr ( attrName ) } '` ;
124
+
125
+ let resolveName = bindings [ name ] || name ;
126
+ // Pre-evaluate the expression for "@" bindings by enclosing in {{ }}
127
+ // some-attr="{{ ::$resolve.someResolveName }}"
128
+ if ( type === '@' )
97
129
return `${ attrName } ='{{${ prefix } $resolve.${ resolveName } }}'` ;
130
+
131
+ // Wire "&" callbacks to resolves that return a callback function
132
+ // Get the result of the resolve (should be a function) and annotate it to get its arguments.
133
+ // some-attr="$resolve.someResolveResultName(foo, bar)"
134
+ if ( type === '&' ) {
135
+ let res = context . getResolvable ( resolveName ) ;
136
+ let fn = res && res . data ;
137
+ let args = fn && services . $injector . annotate ( fn ) || [ ] ;
138
+ let arrayIdxStr = isArray ( fn ) ? `[${ fn . length - 1 } ]` : '' ;
139
+ return `${ attrName } ='$resolve.${ resolveName } ${ arrayIdxStr } (${ args . join ( "," ) } )'` ;
140
+ }
141
+
142
+ // some-attr="::$resolve.someResolveName"
98
143
return `${ attrName } ='${ prefix } $resolve.${ resolveName } '` ;
99
144
} ;
100
145
101
- let attrs = getComponentInputs ( component ) . map ( attributeTpl ) . join ( " " ) ;
146
+ let attrs = getComponentBindings ( component ) . map ( attributeTpl ) . join ( " " ) ;
102
147
let kebobName = kebobString ( component ) ;
103
148
return `<${ kebobName } ${ attrs } ></${ kebobName } >` ;
104
149
} ;
105
-
106
- /**
107
- * Creates a component's template by invoking an injectable provider function.
108
- *
109
- * @param provider Function to invoke via `locals`
110
- * @param {Function } injectFn a function used to invoke the template provider
111
- * @return {string } The template html as a string: "<component-name input1='::$resolve.foo'></component-name>".
112
- */
113
- fromComponentProvider ( provider : IInjectable , params : any , context : ResolveContext ) {
114
- let deps = services . $injector . annotate ( provider ) ;
115
- let providerFn = isArray ( provider ) ? tail ( < any [ ] > provider ) : provider ;
116
- let resolvable = new Resolvable ( "" , < Function > providerFn , deps ) ;
117
- return resolvable . get ( context ) . then ( ( componentName ) => {
118
- return this . fromComponent ( componentName ) ;
119
- } ) ;
120
- } ;
121
150
}
122
151
123
- // Gets all the directive(s)' inputs ('@', '=', and '<')
124
- function getComponentInputs ( name : string ) {
152
+ // Gets all the directive(s)' inputs ('@', '=', and '<') and outputs ('&')
153
+ function getComponentBindings ( name : string ) {
125
154
let cmpDefs = < any [ ] > services . $injector . get ( name + "Directive" ) ; // could be multiple
126
155
if ( ! cmpDefs || ! cmpDefs . length ) throw new Error ( `Unable to find component named '${ name } '` ) ;
127
156
return cmpDefs . map ( getBindings ) . reduce ( unnestR , [ ] ) ;
@@ -143,7 +172,7 @@ interface BindingTuple {
143
172
// for ng 1.3 through ng 1.5, process the component's bindToController: { input: "=foo" } object
144
173
const scopeBindings = ( bindingsObj : Obj ) => Object . keys ( bindingsObj || { } )
145
174
// [ 'input', [ '=foo', '=', 'foo' ] ]
146
- . map ( key => [ key , / ^ ( [ = < @ ] ) [ ? ] ? ( .* ) / . exec ( bindingsObj [ key ] ) ] )
175
+ . map ( key => [ key , / ^ ( [ = < @ & ] ) [ ? ] ? ( .* ) / . exec ( bindingsObj [ key ] ) ] )
147
176
// skip malformed values
148
177
. filter ( tuple => isDefined ( tuple ) && isArray ( tuple [ 1 ] ) )
149
178
// { name: ('foo' || 'input'), type: '=' }
0 commit comments