assertNoRootAppElm(rootAppElm, className)
A convenience function that asserts the suppliedrootAppElm
is NOT
defined.
When this constraint is not met, and error is thrown, after emitting applicable context in the console log.
For more information, please refer to
Injecting DOM Content
.
Param | Type | Description |
---|---|---|
rootAppElm | reactElm | the current react app element root to check. |
className | string | the className on behalf of which this assertion is performed. |
createFeature(name, [enabled], [fassets], [appWillStart], [appInit], [appDidStart], [extendedAspect]) ⇒ Feature
Create a new Feature
object, cataloging
AspectContent
to be consumed by
launchApp()
. Each feature within an application
promotes it's own Feature
object.
For more information, please refer to
Feature & aspect content
.
Please Note this function uses named parameters.
Param | Type | Default | Description |
---|---|---|---|
name | string | the identity of the feature. Feature names
are guaranteed to be unique. Application code can use the Feature
name in various single-source-of-truth operations (see
|
|
[enabled] | boolean | true | an indicator as to whether this
feature is enabled (true) or not (false). When used, this
indicator is typically based on a dynamic expression, allowing
packaged code to be dynamically enabled/disabled at run-time
(please refer to: |
[fassets] | fassets | an optional aspect that promotes feature assets used in
|
|
[appWillStart] | appWillStartCB | an optional
|
|
[appInit] | appInitCB | an optional
|
|
[appDidStart] | appDidStartCB | an optional
|
|
[extendedAspect] | AspectContent | additional aspects, as
defined by the feature-u's Aspect plugins (please refer to:
|
Returns: Feature
- a new Feature object (to be consumed by
launchApp()).
extendFeatureProperty(name, owner)
Extend valid Feature properties to include the supplied name ... used when extending APIs forAspect Cross Communication
.
feature-u keeps track of the agent that owns this extension (using the owner parameter). This is used to prevent exceptions when duplicate extension requests are made by the same owner. This can happen when multiple instances of an aspect type are supported, and also in unit testing.
Throws:
- Error when supplied name is already reserved by a different owner
Param | Type | Description |
---|---|---|
name | string | the property name to allow. |
owner | string | the requesting owner id of this extension request. Use any string that uniquely identifies your utility (such as the aspect's npm package name). |
expandWithFassets(expandWithFassetsCB) ⇒ expandWithFassetsCB
Mark the supplied expandWithFassetsCB()
as a "Managed
Expansion Callback", distinguishing it from other functions (such
as reducer functions).
Features may communicate AspectContent
directly, or
through a expandWithFassetsCB()
. In other words, the
AspectContent
can either be the actual content itself
(ex: reducer, logic modules, etc.), or a function that returns
the content. The latter:
- supports
Cross Feature Communication
(through fassets object injection), and - minimizes circular dependency issues (of ES6 modules).
Managed Expansion Callbacks are used when a fully resolved
Fassets object
is required during in-line code expansion.
They are merely functions that when invoked (under the control of
feature-u), are supplied the Fassets object
and
return the expanded AspectContent
(ex: reducer, logic
modules, etc.).
For more information (with examples), please refer to
Managed Code Expansion
.
The expandWithFassetsCB()
function should conform to the
following signature:
API: expandWithFassetsCB(fassets): AspectContent
Param | Type | Description |
---|---|---|
expandWithFassetsCB | expandWithFassetsCB | the callback
function that when invoked (by feature-u) expands/returns the
desired |
Returns: expandWithFassetsCB
- the supplied expandWithFassetsCB,
marked as a "managed expansion callback".
launchApp(features, [aspects], registerRootAppElm, [showStatus]) ⇒ Fassets
Launch an application by assembling the supplied features, driving
the configuration of the frameworks in use (as orchestrated by the
supplied set of plugable Aspects).
For more information (with examples), please refer to
Launching Your Application
.
Please Note this function uses named parameters.
Param | Type | Description |
---|---|---|
features | Array.<Feature> | the features that comprise this application. |
[aspects] | Array.<Aspect> | the set of plugable Aspects that extend
feature-u, integrating other frameworks to match your specific
run-time stack. When NO Aspects are supplied (an atypical case), only the very basic feature-u characteristics are in effect (like fassets and life-cycle hooks). |
registerRootAppElm | registerRootAppElmCB | the callback hook
that registers the supplied root application element to the specific
React framework used in the app. Because this registration is accomplished by app-specific code,
feature-u can operate in any of the react platforms, such as:
Please refer to |
[showStatus] | showStatusCB | an optional callback hook that communicates a blocking "persistent" status message to the end user. Please refer to |
Returns: Fassets
- the Fassets object used in
cross-feature-communication.
useFassets(p) ⇒ Fassets
| fassets-resource | fassetsProps
A React Hook
that provides functional component access
to feature-u fassets
.
Hooks allow you to "hook into" React state and lifecycle
aspects from functional components. They greatly simplify the UI
implementation as opposed to the alternative of Higher Order
Components (see: withFassets()
).
There are three ways to invoke useFassets()
(examples can be
found at UI Composition
):
Passing NO parameters, returns the
Fassets object
:+ useFassets(): fassets
Passing a
fassetsKey
(a string), returns the resolved fassets resource:+ useFassets('MainPage.*.link@withKeys'): [] ... an array of cataloged links
The
fassetsKey
parameter is identical (in usage) to theFassets.get()
method.Passing a
mapFassetsToPropsStruct
, returns a set of resolved fassetsProps:+ useFassets({ mainBodies: 'MainPage.*.body', mainLinks: 'MainPage.*.link@withKeys' }): { // ... return structure: mainBodies: ... resolved cataloged resource mainLinks: ... ditto }
The
mapFassetsToPropsStruct
allows you to return more than one resolved fassets resources.
SideBar: For useFassets()
to operate,
<FassetsContext.Provider>
must be rendered at the root of your
application DOM. This is really a moot point, because
feature-u automatically performs this initialization, so you
really don't have to worry about this detail (automating
configuration is a Hallmark of feature-u - reducing boilerplate
code).
Param | Type | Description |
---|---|---|
p | string | mapFassetsToPropsStruct | the parameter controlling
what |
Returns: Fassets
| fassets-resource | fassetsProps - see explanation above.
withFassets(mapFassetsToProps, [component]) ⇒ HoC | HoF
Promotes a "wrapped" Component (an HoC - Higher-order Component) that injects fasset props into acomponent
, as specified by the
mapFassetsToProps
parameter. Please refer to the
mapFassetsToPropsStruct
for the details of what this
mapping construct looks like. Examples can be found at
UI Composition
.
Central to this process, a Higher-order Function (HoF) is created
that encapsulates this "mapping knowledge". Ultimately, this
HoF must be invoked (passing component
), which exposes the HoC
(the "wrapped" Component).
+ withFassetsHoF(component): HoC
There are two ways to use withFassets()
:
By directly passing the
component
parameter, the HoC will be returned (internally invoking the HoF). This is the most common use case.By omitting the
component
parameter, the HoF will be returned. This is useful to facilitate "functional composition" (in functional programming). In this case it is the client's responsibility to invoke the HoF (either directly or indirectly) in order to expose the HoC.
SideBar: For withFassets()
to operate,
<FassetsContext.Provider>
must be rendered at the root of your
application DOM. This is really a moot point, because
feature-u automatically performs this initialization, so you
really don't have to worry about this detail (automating
configuration is a Hallmark of feature-u - reducing boilerplate
code).
Please Note this function uses named parameters.
Param | Type | Description |
---|---|---|
mapFassetsToProps | mapFassetsToPropsStruct | mapFassetsToPropsFn | the structure defining the prop/fassetsKey
mapping, from which fasset resources are injected into
|
[component] | ReactComp | optionally, the React Component to be wrapped (see discussion above). |
Returns: HoC | HoF - either the HoC (the "wrapped" Component) when
component
is supplied, otherwise the HoF (see discussion
above).
Examples:
Inject fasset resources from a static structure (
mapFassetsToPropsStruct
), auto wrapping the MainPage Component ...function MainPage({Logo, mainLinks, mainBodies}) { return ( <div> <div> <Logo/> </div> <div> {mainLinks.map( ([fassetsKey, MainLink]) => <MainLink key={fassetsKey}/>)} </div> <div> {mainBodies.map( (MainBody, indx) => <MainBody key={indx}/>)} </div> </div> ); } export default withFassets({ component: MainPage, // NOTE: auto wrap MainPage mapFassetsToProps: { // NOTE: static structure (mapFassetsToPropsStruct) Logo: 'company.logo', // Logo: companyLogoResource, mainLinks: 'MainPage.*.link@withKeys', // mainLinks: [['MainPage.cart.link', cartLinkResource], // ['MainPage.search.link', searchLinkResource]], mainBodies: 'MainPage.*.body' // mainBodies: [cartBodyResource, searchBodyResource], } });
Inject fasset resources from a functional directive (
mapFassetsToPropsFn
), returning the HoF - immediately invoked ...function MainPage({mainLinks, mainBodies}) { return ( ... same as prior example ); } export default withFassets({ mapFassetsToProps(ownProps) { // NOTE: functional directive (mapFassetsToPropsFn) ... some conditional logic based on ownProps return { Logo: 'company.logo', // Logo: companyLogoResource, mainLinks: 'MainPage.*.link@withKeys', // mainLinks: [['MainPage.cart.link', cartLinkResource], // ['MainPage.search.link', searchLinkResource]], mainBodies: 'MainPage.*.body' // mainBodies: [cartBodyResource, searchBodyResource], }; } })(MainPage); // NOTE: immediately invoke the HoF, emitting the wrapped MainPage Component
createAspect(name, [genesis], [validateFeatureContent], [expandFeatureContent], [assembleFeatureContent], [assembleAspectResources], [initialRootAppElm], [injectRootAppElm], [injectParamsInHooks], [config], [additionalMethods]) ⇒ Aspect
Create an Aspect
object, used to extend feature-u.
The Aspect
object promotes a series of life-cycle
methods that feature-u invokes in a controlled way. This
life-cycle is controlled by launchApp()
... it is
supplied the Aspects, and it invokes their methods.
The essential characteristics of a typical Aspect
life-cycle is to:
- accumulate
AspectContent
across all features - perform the desired setup and configuration
- expose the framework in some way (by injecting a component in the
root DOM, or some
Aspect Cross Communication
mechanism)
The Extending feature-u
section provides more insight on how
Aspect
s are created and used.
Aspect Plugins have NO one specific method that is required. Rather
the requirement is to specify something (so as to not have an
empty plugin that does nothing).
Please refer to the "No Single Aspect Method
is Required" discussion in the
Aspect Life Cycle Methods
.
Please Note this function uses named parameters. The order in which these items are presented represents the same order they are executed.
Param | Type | Description |
---|---|---|
name | string | the For example: an As a result, Aspect names cannot clash with built-in aspects, and
they must be unique (across all aspects that are in-use). The |
[genesis] | genesisMeth | a Life Cycle Hook invoked one time, at the very beginning of the app's start up process. This hook can perform Aspect related initialization and validation: |
[validateFeatureContent] | validateFeatureContentMeth | a validation hook allowing this aspect to verify it's content on the supplied feature (which is known to contain this aspect). |
[expandFeatureContent] | expandFeatureContentMeth | an
aspect expansion hook, defaulting to the algorithm defined
by This function rarely needs to be overridden. It provides a hook to aspects that need to transfer additional content from the expansion function to the expanded content. |
[assembleFeatureContent] | assembleFeatureContentMeth | the
Aspect method that assembles content for this aspect across all
features, retaining needed state for subsequent ops. This method is typically the primary task that is accomplished by most aspects. |
[assembleAspectResources] | assembleAspectResourcesMeth | an
Aspect method that assemble resources for this aspect
across all other aspects, retaining needed state for subsequent
ops. This hook is executed after all the aspects have assembled their
feature content (i.e. after
|
[initialRootAppElm] | initialRootAppElmMeth | a
callback hook that promotes some characteristic of this aspect
within the The |
[injectRootAppElm] | injectRootAppElmMeth | a
callback hook that promotes some characteristic of this aspect
within the The |
[injectParamsInHooks] | injectParamsInHooksMeth | an
Aspect method that promotes |
[config] | Any | a sub-object that can be used for
any type of configuration that a specific Aspect may need (see:
|
[additionalMethods] | Any | additional methods (proprietary to
specific Aspects), supporting
|
Returns: Aspect
- a new Aspect object (to be consumed by launchApp()
).
extendAspectProperty(name, owner)
Extend valid Aspect properties to include the supplied name ... used when extending APIs forAspect Cross Communication
.
feature-u keeps track of the agent that owns this extension (using the owner parameter). This is used to prevent exceptions when duplicate extension requests are made by the same owner. This can happen when multiple instances of an aspect type are supported, and also in unit testing.
Throws:
- Error when supplied name is already reserved by a different owner
Param | Type | Description |
---|---|---|
name | string | the property name to extend. |
owner | string | the requesting owner id of this extension request. Use any string that uniquely identifies your utility (such as the aspect's npm package name). |
Fassets : Object
Thefassets
object (emitted from launchApp()
) is
an accumulation of public feature assets gathered from all
features. It facilitates Cross Feature Communication
by promoting
the public resources of any given feature.
SideBar: The term fassets
is a play on words. While it is
pronounced "facet" and is loosely related to this term, it is
spelled fassets (i.e. feature assets).
There are 3 different ways to reference the resources contained
in the fassets
object:
You may directly dereference them. As an example, an '
action.openView
' resource can be dereferenced as follows:fassets.action.openView('mainView');
You may use the
Fassets.get()
method, which can collect multiple resources (usingWildcards
).Your UI components may indirectly access
fassets
resources through thewithFassets()
Higher-order Component (HoC).
SideBar: There are several ways to get a handle to the
fassets
object (see
Obtaining fassets object
).
For more information, please refer to Cross Feature Communication
and
Basic Concepts: fassets
.
- Fassets : Object
- .get(fassetsKey) ⇒ resource | Array.<resource>
- .hasFeature(featureName) ⇒ boolean
Fassets.get(fassetsKey) ⇒ resource | Array.<resource>
Get (i.e. fetch) the resource(s) corresponding to the suppliedfassetsKey
.
The fassets.get()
method is an alternative to directly
dereferencing the fassets
object ... the advantage being:
it can accumulate a series of resources (when
Wildcards
are used)and it can more gracefully return undefined at any level within the federated namespace path
Regarding the fassetsKey
:
It is case-sensitive (as are the defined resources).
It may contain
Wildcards
(*
), resulting in a multiple resources being returned (a resource array), matching the supplied patternMatches are restricted to the actual fassetKeys registered through the
fassets aspect
define
/defineUse
directives. In other words, the matching algorithm will not drill into the resource itself (assuming it is an object with depth).The special dot keyword (
'.'
) will return the fassets object itself (in the same tradition as "current directory").'@withKeys'
:In some cases, you may wish to know the corresponding
fassetsKey
of the returned resource. This is especially true when multiple resources are returned (using wildcards). As an example, JSX requires unique keys for array injections (thefassetsKey
is a prime candidate for this, since it is guaranteed to be unique).To accomplish this, simply suffix the
fassetsKey
with the keyword:'@withKeys'
. When this is encountered, the resource returned is a two-element array:[fassetsKey, resource]
. SideBar: We use this suffix technique (as opposed to an additional parameter) to be consistent with howwithFassets()
operates.
SideBar: The fassets.get()
method is the basis of both the
useFassets()
React Hook
, and the
withFassets()
Higher-order Component (HoC).
Param | Type | Description |
---|---|---|
fassetsKey | string | the key of the resource(s) to fetch.
This may include wildcards ( |
Returns: resource | Array.<resource> - the requested fassets resource(s).
without wildcards, a single resource is returned (
undefined
for none).'a.b.c': abcResource 'a.b.c@withKeys': ['a.b.c', abcResource]
with wildcards, the return is a resource array, in order of feature expansion (empty array for none).
'a.*': [ab1Resource, ab2Resource, ...] 'a.*@withKeys': [ ['a.b1', ab1Resource], ['a.b2', ab2Resource], ... ]
Fassets.hasFeature(featureName) ⇒ boolean
Return an indicator as to whether the supplied feature is active or not.Note: As an alternative to using this method, you can conditionally reason over the existence of "well-known fasset resources" specific to a given feature.
Param | Type | Description |
---|---|---|
featureName | string | the name of the feature to check. |
Returns: boolean - true: is active, false: is not active (or doesn't exist).
Feature : Object
The Feature object is merely a lightweight container that holdsAspectContent
of interest to feature-u.
Each feature within an application promotes a Feature object (using
createFeature()
) which catalogs the aspects of that feature.
Ultimately, all Feature objects are consumed by
launchApp()
.
Feature content are simple key/value pairs (the key being an Aspect.name with values of AspectContent). These aspects can either be built-in (from core feature-u), or extensions.
Here is an example:
export default createFeature({
name: 'featureA', // builtin aspect (name must be unique across all features within app)
enabled: true, // builtin aspect enabling/disabling feature
fassets: { // builtin aspect promoting Public Face - Cross Feature Communication
define: {
'api.openA': () => ...,
'api.closeA': () => ...,
},
},
appWillStart: (...) => ..., // builtin aspect (Application Life Cycle Hook)
appInit: (...) => ..., // ditto
appDidStart: (...) => ..., // ditto
reducer: ..., // feature redux reducer (extended aspect from the feature-redux plugin)
logic: ..., // feature logic modules (extended aspect from the feature-redux-logic plugin)
});
For more information, please refer to
Feature & aspect content
.
appWillStartCB ⇒ reactElm | void
An optionalApplication Life Cycle Hook
invoked one time, very
early in the app startup process.
This life-cycle hook can do any type of general app-specific initialization (for example initializing a PWA service worker).
In addition, it can optionally inject static content in the app's
DOM root. Any return is interpreted as the app's new rootAppElm
(an accumulative process). IMPORTANT: When this is used, the
supplied curRootAppElm
MUST be included as part of this
definition (accommodating the accumulative process of other feature
injections)! More information is available at
Injecting DOM Content
For more information (with examples), please refer to the
Guide's appWillStart
.
Please Note this function uses named parameters.
Param | Type | Description |
---|---|---|
fassets | Fassets | the Fassets object used in cross-feature-communication. |
curRootAppElm | reactElm | the current react app element root. |
Returns: reactElm | void - optionally, new top-level content (which in turn
must contain the supplied curRootAppElm
). Use a void return
when top-level content is unchanged.
appInitCB ⇒ Promise | void
An optionalApplication Life Cycle Hook
invoked one time, later in
the app startup process. It supports blocking async
initialization.
This hook is invoked when the app is nearly up-and-running.
The
React Registration
has already occurred (via theregisterRootAppElm()
callback). As a result, you can rely on utilities that require an app-specificrootAppElm
to exist.You have access to the
getState()
anddispatch()
function, assuming you are usingredux
(when detected by feature-u's plugable aspects).These parameters are actually injected by the
feature-redux
Aspect, and are examples of what can be injected by any Aspect (please refer your specific Aspect's documentation to determine other parameters).
Just like the appWillStart()
hook, you may perform any
type of general initialization that is required by your feature.
However the hallmark of this hook is you can block for any asynchronous initialization to complete. By simply returning a promise, feature-u will wait for the process to complete.
The user is kept advised of any long-running async processes. By
default an 'initializing feature: {feature.name}'
message is
used, but you can customize it through the supplied
showStatus()
function parameter.
For more info with examples, please see the Guide's
appInit
.
Please Note this function uses named parameters.
Param | Type | Description |
---|---|---|
showStatus | showStatusCB | the function that (when invoked) will communicate a blocking "persistent" status message to the end user. |
fassets | Fassets | the Fassets object used in cross-feature-communication. |
[getState] | Any | the redux function returning the top-level app state (when redux is in use). |
[dispatch] | function | the redux dispatch() function (when redux is in use). |
[injectedAspectParams] | any | additional parameters
injected by Aspect plugins (please refer your specific Aspect's
documentation to determine other parameters). The |
Returns: Promise | void - optionally, a promise (for asynchronous processes) - and feature-u will wait for the process to complete. Use a void return (for synchronous processes) - and no blocking will occur.
appDidStartCB ⇒
An optionalApplication Life Cycle Hook
invoked one time,
once the app startup process has completed.
This life-cycle hook can be used to trigger "the app is running" events. A typical usage is to "kick start" some early application logic.
Because the app is up-and-running at this time, you have access to
the getState()
and dispatch()
function ... assuming you are using
redux
(when detected by feature-u's plugable aspects).
These parameters are actually injected by the
feature-redux
Aspect, and are examples of what can be
injected by any Aspect (please refer your specific Aspect's
documentation to determine other parameters).
For more info with examples, please see the Guide's
appDidStart
.
Please Note this function uses named parameters.
Param | Type | Description |
---|---|---|
fassets | Fassets | the Fassets object used in cross-feature-communication. |
[getState] | Any | the redux function returning the top-level app state (when redux is in use). |
[dispatch] | function | the redux dispatch() function (when redux is in use). |
[injectedAspectParams] | any | additional parameters
injected by Aspect plugins (please refer your specific Aspect's
documentation to determine other parameters). The |
Returns: void
fassets : BuiltInAspect
A builtin aspect that publicly promotes feature-based resources calledfassets
(feature assets). These resources are the basis
of Cross Feature Communication
. You can think of this as the Public Face
of a feature.
SideBar: The term fassets
is a play on words. While it is
pronounced "facet" and is loosely related to this term, it is
spelled fassets (i.e. feature assets).
Feature resources are accumulated across all features, and exposed
through the Fassets object
. They can also be referenced
via the withFassets()
HoC.
The fassets
aspect can both define resources, and/or declare a
resource contract (i.e. the intention to use a set of fasset
resources). This is accomplished via three separate fassets
directives: define
, use
, and defineUse
. A good summary of
these directives can be found at
fassets Recap: Push or Pull
.
define: define public resources, held in the
Fassets object
fassets: { define: { '{fassetsKey}': {fassetsValue} ... NOTES: - fassetsKey MUST be unique - are case-sensitive - may contain federated namespace (via dots ".") ... normalized in fassets object ... ex: 'MainPage.launch' - may be any valid JS identifier (less $ support) - may NOT contain wildcards ... i.e. must be defined completely // examples ... 'openView': actions.view.open, // fassets.openView(viewName): Action // federated namespace example 'selector.currentView': selector.currentView, // fassets.selector.currentView(appState): viewName // UI Component example 'MainPage.cart.link': () => <Link to="/cart">Cart</Link>, 'MainPage.cart.body': () => <Route path="/cart" component={ShoppingCart}/>, } }
use: specify public resource keys that will be used by the containing feature (i.e. a resource contract)
fassets: { use: [ '{fassetsKey}', -or- ['$fassetsKey', {required: true/false, type: $validationFn}], ... NOTES: - each key will be supplied by other features - this is a communication to other features (i.e. a contract) ... saying: I plan to "use" these injections HOWEVER: feature-u cannot strictly enforce this usage ... enclosed feature should reference this {fassetsKey} through fassets.get(), or withFassets() - is case-sensitive - may contain federated namespace (with dots ".") ... ex: 'MainPage.launch' - may be any valid JS identifier (less $ support) - may contain wildcards (with "*") ... ex: 'MainPage.*.link' // examples ... 'MainPage.launch', // may contain wildcards ... 'MainPage.*.link', 'MainPage.*.body', // optionally supply options object, controlling optionality and data types ['MainPage.*.link', { required: true, type: any }], // same as DEFAULTS ['MainPage.*.link', { required: false, }], // optional of any type ['MainPage.*.link', { type: comp }], // required of react component type ['MainPage.*.link', { required: false, type: comp }], // optional of react component type ] }
defineUse: define public resources specified by other features (via the
use
directive)fassets: { defineUse: { '{fassetsKey}': {fassetsValue} ... NOTES: - this is identical to fassets.define EXCEPT: - it MUST MATCH a fassets.use directive ... using this directive, feature-u will perform additional validation to unsure these entries match a use contract // examples ... 'MainPage.cart.link': () => <Link to="/cart">Cart</Link>, 'MainPage.cart.body': () => <Route path="/cart" component={ShoppingCart}/>, } }
For more information, please refer to Cross Feature Communication
,
Fassets object
, the withFassets()
HoC,
and the fassets Recap: Push or Pull
.
expandWithFassetsCB ⇒ AspectContent
A "managed expansion callback" (defined by
expandWithFassets()
) that when invoked (by feature-u)
expands and returns the desired AspectContent
.
For more information (with examples), please refer to
Managed Code Expansion
.
Param | Type | Description |
---|---|---|
fassets | Fassets | the Fassets object used in cross-feature-communication. |
Returns: AspectContent
- The desired AspectContent (ex: reducer,
logic module, etc.).
fassetValidations : Object
A pre-defined container of fasset validation functions, which can be employed in thefassets aspect
use
directive.
This allows the use
directive to specify data type and content
validation constraints.
These validations are available as a convenience. Additional validations can be created as needed.
The validation API should adhere to the following signature:
+ fassetValidationFn(fassetsValue): string || null
A return value of null represents a valid value, while a string specifies a validation error that feature-u will format as follows (see ${returnStr}):
VALIDATION ERROR in resource: '${fassetsKey}',
expecting: ${returnStr} ...
resource defined in Feature: '${resource.definingFeature}',
usage contract '${useKey}' found in Feature: '${featureName}'
The following pre-defined validations are promoted through fassetValidations
:
any
: any type (except undefined)comp
: a react componentfn
: a functionstr
: a stringbool
: a boolean
Example:
createFeature({
fassets: {
use: [
'MainPage.*.link', // DEFAULT: required of type any
['MainPage.*.body', {required: false, type: fassetValidations.comp}],
],
},
});
registerRootAppElmCB ⇒
ThelaunchApp()
callback hook that registers the
supplied root application element to the specific React framework
used in the app.
Because this registration is accomplished by app-specific code,
feature-u can operate in any of the React platforms, such as:
react
web, react-native
,
expo
, etc.
Please refer to React Registration
for more
details and complete examples.
Param | Type | Description |
---|---|---|
rootAppElm | reactElm | the root application element to be registered. |
fassets | Fassets | the Fassets object used in cross-feature-communication (rarely needed except to allow client to inject their own FassetsContext.Provider for a null rootAppElm). |
Returns: void
showStatusCB ⇒
The optionallaunchApp()
callback hook that communicates
a blocking "persistent" status message to the end user.
These status messages originate from the blocking that occurs in
the asynchronous processes managed by the Feature.appInit()
life-cycle-hook.
By design feature-u has no ability to manifest messages to the
end user, because this is very app-specific in styling and other
heuristics. By default (when NO showStatus
parameter is
supplied, feature-u will simply console log these messages.
A typical manifestation of this callback is to display a running
persistent SplashScreen, seeded with the supplied message. The
SplashScreen should be taken down when NO message is supplied
(i.e. ''
).
Please refer to Feature.appInit()
for more details and
examples.
Param | Type | Description |
---|---|---|
[msg] | string | the "persistent" message to display. When
NO message is supplied (i.e. |
[err] | Error | an optional error to communicate to the user. |
Returns: void
mapFassetsToPropsStruct : Object
A structure (used bywithFassets()
and useFassets()
) defining a
prop/fassetsKey mapping, from which fasset resources are injected
into a Component. Please see UI Composition
for examples.
The injected Component properties will reference the fasset
resource corresponding to the fassetsKey
.
Each fassetsKey
is case-sensitive (as are the defined resources).
Matches are restricted to the actual fassetKeys registered through
the fassets aspect
define
/defineUse
directives. In
other words, the matching algorithm will not drill into the
resource itself (assuming it is an object with depth).
The special dot keyword ('.'
) will yield the fassets object
itself (in the same tradition as "current directory"). This is
useful if you wish to inject fassets into downstream processes
(such as redux connect()
via it's ownProps
).
Wildcards
Wildcards
(*
) are supported in the
fassetsKey, accumulating multiple resources (a resource array),
matching the supplied pattern:
without wildcards, a single resource is injected (
undefined
for none).with wildcards, a resource array is injected, in order of feature expansion (empty array for none).
Example ...
mapFassetsToProps: {
Logo: 'company.logo',
// Logo: companyLogoResource,
// NOTE: wildcard usage ...
mainLinks: 'MainPage.*.link',
// mainLinks: [cartLinkResource, searchLinkResource],
mainBodies: 'MainPage.*.body'
// mainBodies: [cartBodyResource, searchBodyResource],
}
@withKeys
In some cases, you may wish to know the corresponding
fassetsKey
of the returned resource. This is especially true
when multiple resources are returned (using wildcards).
As an example, React requires a key
attribute for array
injections (the fassetsKey
is a prime candidate for this, since
it is guaranteed to be unique).
To accomplish this, simply suffix the fassetsKey
with the
keyword: '@withKeys'
. When this is encountered, the resource
returned is a two-element array: [fassetsKey, resource]
.
Example ...
mapFassetsToProps: {
Logo: 'company.logo',
// Logo: companyLogoResource,
mainLinks: 'MainPage.*.link@withKeys', // NOTE: @withKeys directive
// mainLinks: [['MainPage.cart.link', cartLinkResource],
// ['MainPage.search.link', searchLinkResource]],
mainBodies: 'MainPage.*.body'
// mainBodies: [cartBodyResource, searchBodyResource],
}
This topic is discussed in more detail in: React Keys (in array processing)
.
mapFassetsToPropsFn ⇒ mapFassetsToPropsStruct
A function (used by withFassets()
) that returns a
mapFassetsToPropsStruct
, defining a prop/fassetsKey
mapping, from which fasset resources are injected into a Component.
Param | Type | Description |
---|---|---|
ownProps | obj | the outlying properties supplied to the connected component. |
Returns: mapFassetsToPropsStruct
- the structure defining a
prop/fassetsKey mapping, from which fasset resources are injected
into a Component.
Aspect : Object
Aspect objects (emitted fromcreateAspect()
) are used to
extend feature-u.
The Aspect object promotes a series of life-cycle methods that
feature-u invokes in a controlled way. This life-cycle is
controlled by launchApp()
... it is supplied the
Aspects, and it invokes their methods.
Typically Aspects are packaged separately (as an external npm feature-u extension), although they can be created locally within a project (if needed).
For more information, please refer to
Extendable aspects
and
Extending feature-u
.
AspectContent : Any
The content (or payload) of anAspect
, specified
within a Feature
.
The content type is specific to the Aspect. For example, a redux
Aspect assembles reducers (via Feature.reducer
), while a
redux-logic Aspect gathers logic modules (via Feature.logic
),
etc.
AspectContent can either be defined from built-in aspects
(via core feature-u), or extensions (from
Aspect
).
An Aspect
object extends feature-u by accumulating
information of interest from Feature
objects (indexed
by the Aspect name).
Note: Whenever AspectContent definitions require the
Fassets object
at code expansion time, you can wrap the
definition in a expandWithFassets()
function. In other
words, your aspect content can either be the actual content itself
(ex: a reducer), or a function that returns the content.
For more information, please refer to
Feature & aspect content
.
genesisMeth ⇒ string
A Life Cycle Hook invoked one time, at the very beginning of the app's start up process.Antiquated Note:
The
genesis()
hook is somewhat antiquated, relegated to Aspects that are promoted as singletons. In this scenario, client-side configuration could be introduced after instantiation (by adding content toAspect.config
), while still allowing initialization and validation to occur early in the startup process (via thisgenesis()
hook).A better alternative to the
genesis()
hook is to promote yourCustom Aspect Plugins
as non-singletons, where initialization and validation can be directly promoted though the plugin constructor.
The genesis()
hook can perform Aspect related initialization and
validation:
initialization: It is possible to to register proprietary Aspect/Feature APIs in the
genesis()
hook ... viaextendAspectProperty()
andextendFeatureProperty()
(please see:Aspect Cross Communication
andCross Feature Communication
).The preferred place to do this initialization is in the plugin constructor (see Antiquated Note above).
validation: It is possible to perform Aspect validation in the
genesis()
hook ... say for required configuration properties injected by the client after instantiation. This is the reason for the optional return string.The preferred place to do validation is in the plugin constructor, gathering this information as constructor parameters (see Antiquated Note above).
API: genesis(): string
Returns: string - an error message when self is in an invalid state
(falsy when valid). Because this validation occurs under the
control of launchApp()
, any message is prefixed with:
'launchApp() parameter violation: '
.
validateFeatureContentMeth ⇒ string
A validation hook allowing this aspect to verify it's content on the supplied feature.API: validateFeatureContent(feature): string
Param | Type | Description |
---|---|---|
feature | Feature | the feature to validate, which is known to contain this aspect. |
Returns: string - an error message string when the supplied feature
contains invalid content for this aspect (falsy when valid).
Because this validation conceptually occurs under the control of
createFeature()
, any message is prefixed with:
'createFeature() parameter violation: '
.
expandFeatureContentMeth ⇒ string
Expand self'sAspectContent
in the supplied feature,
replacing that content (within the feature). Once expansion is
complete, feature-u will perform a delayed validation of the
expanded content.
API: expandFeatureContent(fassets, feature): string
The default behavior simply implements the expansion algorithm
defined by expandWithFassets()
:
feature[this.name] = feature[this.name](fassets);
This default behavior rarely needs to change. It however provides
a hook for aspects that need to transfer additional content from
the expansion function to the expanded content. As an example, the
reducer
aspect must transfer the slice property from the
expansion function to the expanded reducer.
Param | Type | Description |
---|---|---|
fassets | Fassets | the Fassets object used in feature cross-communication. |
feature | Feature | the feature which is known to contain
this aspect and is in need of expansion (as defined by
|
Returns: string - an optional error message when the supplied
feature contains invalid content for this aspect (falsy when
valid). This is a specialized validation of the expansion
function, over-and-above what is checked in the standard
validateFeatureContent()
hook.
assembleFeatureContentMeth ⇒
The Aspect method that assembles content for this aspect across all features, retaining needed state for subsequent ops. This method is typically the primary task that is accomplished by most aspects.API: assembleFeatureContent(fassets, activeFeatures): void
Param | Type | Description |
---|---|---|
fassets | Fassets | the Fassets object used in feature cross-communication. |
activeFeatures | Array.<Feature> | The set of active (enabled) features that comprise this application. |
Returns: void
assembleAspectResourcesMeth ⇒
An Aspect method that assembles resources for this aspect across all other aspects, retaining needed state for subsequent ops. This hook is executed after all the aspects have assembled their feature content (i.e. afterassembleFeatureContent()
).
API: assembleAspectResources(fassets, aspects): void
This is an optional second-pass (so-to-speak) of Aspect data
gathering, that facilitates
Aspect Cross Communication
. It allows an
extending aspect to gather resources from other aspects, using an
additional API (ex: Aspect.getXyz()
).
Param | Type | Description |
---|---|---|
fassets | Fassets | the Fassets object used in feature cross-communication. |
aspects | Array.<Aspect> | The set of feature-u Aspect objects used in this this application. |
Returns: void
initialRootAppElmMeth ⇒ reactElm
A callback hook that promotes some characteristic of this aspect within therootAppElm
... the top-level react DOM that
represents the display of the entire application.
API: initialRootAppElm(fassets, curRootAppElm): rootAppElm
The Defining rootAppElm
section highlights when
to use initialRootAppElm()
verses
injectRootAppElm()
.
NOTE: When this hook is used, the supplied curRootAppElm MUST be included as part of this definition!
Param | Type | Description |
---|---|---|
fassets | Fassets | the Fassets object used in feature cross-communication. |
curRootAppElm | reactElm | the current react app element root. |
Returns: reactElm - a new react app element root (which in turn must contain the supplied curRootAppElm), or simply the supplied curRootAppElm (if no change).
injectRootAppElmMeth ⇒ reactElm
A callback hook that promotes some characteristic of this aspect within therootAppElm
... the top-level react DOM that
represents the display of the entire application.
API: injectRootAppElm(fassets, curRootAppElm): rootAppElm
The Defining rootAppElm
section highlights when
to use initialRootAppElm()
verses
injectRootAppElm()
.
NOTE: When this hook is used, the supplied curRootAppElm MUST be included as part of this definition!
Param | Type | Description |
---|---|---|
fassets | Fassets | the Fassets object used in feature cross-communication. |
curRootAppElm | reactElm | the current react app element root. |
Returns: reactElm - a new react app element root (which in turn must contain the supplied curRootAppElm), or simply the supplied curRootAppElm (if no change).
injectParamsInHooksMeth ⇒ namedParams
An Aspect method that promotesnamedParams
into the
feature's Application Life Cycle Hooks
, from this aspect. This
hook is executed after all aspects have assembled their feature
content (i.e. after assembleFeatureContent()
).
Here is a namedParams
example from a redux aspect, promoting it's
state and dispatch functions:
{getState, dispatch}
API: injectParamsInHooks(fassets): namedParams
Any aspect may promote their own set of namedParams
. feature-u
will insure there are no name clashes across aspects (which results
in an exception). If your parameter names have a high potential
for clashing, a best practice would be to qualify them in some
way to better insure uniqueness.
Param | Type | Description |
---|---|---|
fassets | Fassets | the Fassets object used in feature cross-communication. |
Returns: namedParams - a plain object that will be injected (as
named parameters) into the feature's Application Life Cycle Hooks
,
from this aspect.