App§
The App is the only semblance of global context that exists in Janus. It does
not actually live globally, it is carefully (and automatically) passed around to
where it is needed (mostly on the View layer).
It performs two primary functions:
- It carries the View library, which contains all the Views in your application registered against their target subject classtypes, so that Janus knows which Views to render given the data it has.
- It carries the Resolvers library, which contain all the Resolvers
for all the Requests in your application, and the
#resolvermethod which defines the resolution process, so that Janus knows how to request and cache the data you need.
Almost everything that could be considered "magic" in Janus lives inside of App.
For a thorough account of App and its responsibilities, please see its full
chapter.
Creation§
App derives from Model, so in most respects it works exactly the same way.
@constructor§
new App(): App
new App(data: Object): App
As with Model@constructor, you can construct an App with
or without data. Apart from the data keys views and resolvers, which are used
to store the View and Resolver libraries respectively, Janus does not make use
of Model data at all, and you are free to manipulate the data however you wish.
Because App has something resembling global context, it can be a convenient place
to store application context like localization language, user authentication, and
other commonly-needed information.
return new App({ locale: { major: 'en', minor: 'US' } });Application Views§
.views§
.views: Library
A convenience getter which is exactly equivalent to calling app.get_('views').
const app = new App();
return app.views;#view§
.view(subject: *): View?
.view(subject: *, criteria: Object, options: Object): View?
The #view method returns a new instance of a View appropriate for the given
subject, if one can be found. If criteria is given, it is passed along to the
underlying Views Library as part of the search. If options is given,
it is passed along to the View constructor as the second parameter.
Behind the scenes, App does some additional work beyond what is listed above:
- It ensures that the
optionsgiven to theViewconstructor has anappvalue of itself. If you given an explicitoptionsand it already has anapp, that value is used instead. - It performs manual and auto-resolution
on the
subject, if it has the appropriate facilities to do so. - It emits a
createdViewevent upon itself, with the instantiatedViewas the only event parameter.
Typically, this method is called on your behalf, by the render
mutator.
For more information about this process, we recommend reading the theory chapter that covers it in detail.
class SampleModel extends Model {}
const SampleView = DomView.build(
$('<p/>'),
find('p').text(from('message'))
);
const app = new App();
app.views.register(SampleModel, SampleView);
return app.view(new SampleModel({ message: 'hello!' }));Application Resolvers§
.resolvers§
.resolvers: Library
A convenience getter which is exactly equivalent to calling app.get_('resolvers').
const app = new App();
return app.resolvers;#resolve§
.resolve(request: Request): Varying[types.result]?
Given a request instance, App consults its resolver process to
attempt to resolve the request into a Varying[types.result] value. If it cannot
do so, a nullish value will be returned.
If a successful resolution occurs, a resolvedRequest event will be emitted on
the App instance, with request, result as the event arguments.
Typically, this method is called on your behalf, by Reference attribute.
The default implementation of #resolver is to consult the App Resolver library,
as demonstrated in the following sample.
const alwaysTheAnswer = (request) => Varying.of(types.result.success(42));
class SampleRequest extends Request {}
const app = new App();
app.resolvers.register(SampleRequest, alwaysTheAnswer);
return app.resolve(new SampleRequest());Extending App (Overrides)§
The primary reason you will have for extending App will be to override the #resolver
method, in order to define your own resolver process.
#resolver§
Resolver: (Request → Varying[types.result]?) ⇒ .resolver(): Resolver
The #resolver method is usually only ever called once, by App itself. It is
expected to return some Resolver function which encapsulates your desired Request
resolution process: in particular, resolution sources and result caching.
More detail can be found in the chapter on this subject
as well as on the Resolver API reference page.
The sample for the MemoryCache resolver cache demonstrates
an override of the #resolver method in a relatively realistic context.