UI
Baked introduces a metadata generation template which can be used to transform the business domain to UI metadata to be used in dynamic UI applications.
app.Layers.AddUi();
UiLayer provides IComponentDescriptor, IComponentSchema, IData types for
building UI page metadata. A page metadata will have a component descriptor
(represented by a ComponentDescriptor instance) at the top in the hierarchy
containing Type, Schema, Name and Data properties.
UI Application
Generated metadata is rendered by @mouseless/baked, a Nuxt module. Create a
Nuxt application, install the module and add it to nuxt.config.ts;
npm install @mouseless/baked
import Aura from "@primeuix/themes/aura";
export default defineNuxtConfig({
modules: ["@mouseless/baked"],
baked: {
apiBaseURL: "http://localhost:5151",
primevue: {
theme: Aura
}
}
});
Generated UI metadata files are saved to Ui folder at $(OutDir) of your
project. To use them in the UI application, set below properties in your
application's .csproj file, so that they are copied to .baked folder of the
UI application on every build;
<PropertyGroup>
<CopyComponentDescriptors>true</CopyComponentDescriptors>
<UiAppDir>$(ProjectDir)..\admin</UiAppDir>
</PropertyGroup>
UI layer is only added when a theme is set, e.g.,
Monolithrecipe adds it withoptions: o => o.Theme(c => c.Default(...)), see Theme
Configuration Targets
This layer provides AppDescriptor, ComponentExports, LayerDescriptors
and PageDescriptors configuration target for registering pages using
ComponentDescriptor instances.
AppDescriptor
This target is provided in Generate phase. To configure it in a feature;
configurator.Ui.ConfigureAppDescriptor(app =>
{
...
});
ComponentExports
This target is provided in Generate phase. To configure it in a feature;
configurator.Ui.ConfigureComponentExports(exports =>
{
...
});
LayoutDescriptors
This target is provided in Generate phase. To configure it in a feature;
configurator.Ui.ConfigureLayoutDescriptors(layouts =>
{
...
});
PageDescriptors
This target is provided in Generate phase. To configure it in a feature;
configurator.Ui.ConfigurePageDescriptors(pages =>
{
...
});
To access the localization function from a feature use below extension method.
configurator.Ui.UsingLocalization(l => { // use this function to add that key to UI project; // l("A localized message") ... });Each call to this function will result
UiLayerto bring that value of given key for every language to the UI project under.baked/folder, e.g.,.baked/locale.en.jsonfile.
To access which keys are going to be brought to UI project from a feature, you can use
UsingLocaleTemplatefunction.configurator.Ui.UsingLocaleTemplate(localeTemplate => { ... });
Generator System
To generate a page descriptor from a domain model, we provide a generator system that is added through domain model conventions. There are two generator attributes for this purpose;
ComponentGenerator<TComponentSchema>Generator<TSchema>
The page generator starts with a Page component path to render a domain model
into a ComponentDescriptor<TComponentSchema> instance. To add a component to a
domain model, for instance to a type, use the AddTypeComponent extension of
IDomainModelConventionCollection;
configurator.Domain.ConfigureConventions(conventions =>
{
conventions.AddTypeComponent(
when: c => c.Type...,
where: cc => cc.Path.EndsWith("Page"),
component: (c, cc) => ...
);
});
when:is a predicate function that takesTypeModelMetadataContext cas a parameter and serves as a query to filter typeswhere:is a predicate funtion that takesComponentContext ccas a parameter and is a selector that determines at which component path this convention should be appliedcomponent:is a generator function that takesTypeModelMetadataContext candComponentContext ccas parameters, and is expected to return aComponentDescriptor<TComponentSchema>instance, such asComponentDescriptor<TabbedPage>
AddPropertyComponent, AddMethodComponent and AddParameterComponent
extensions works the same way.
Once you add a component for a domain model at a specific component path, it
will return that component instance when asked at that component path. You may
use GenerateComponent extension to ask for a child component during a page
generation. For example, below call asks for a component at ../title path for
the type;
type.GenerateComponent(cc.Drill("Title"))
ComponentContexthas aDrill(...)method that enables you to navigate deeper into component paths.
GenerateRequiredComponentis also provided to ensure that a component exists at the speficied path for the domain model. Otherwise, it will cause a generation error during post-build phase ofdotnet build.
For non-component schemas, similar extensions are provided for domain models;
AddTypeSchemaAddPropertySchemaAddMethodSchemaAddParameterSchema
Use these extensions to associate domain models with non-component schemas such
as Tab or Input. Once you add a schema for a domain model, you can access it
using GenerateSchema<TSchema> or GenerateSchemas<TSchema> extension methods.
GenerateRequiredSchema<TSchema>is also provided to ensure that a schema exists at that path for that domain model. Otherwise, it will cause a generation error during post-build phase ofdotnet build.
Configuring Existing Schemas
To add a convention that configures an existing schema, there are
Edit...Component<TComponentSchema> and Edit...Schema<TSchema> helpers. Edit
conventions works exactly the same way add/set conventions. In addition, to make
sure there is a schema to configure, configuration conventions automatically
filter out the domain models that don't have the given TComponentSchema or
TSchema at the expected component path.
configurator.Domain.ConfigureConventions(conventions =>
{
// This convention will automatically apply only to the types that have a
// `SimplePage` component
conventions.EditTypeComponent<SimplePage>(
component: sp =>
{
sp.Title = ...;
}
);
});
Bake.vue
Bake.vue is the core utility component that interprets a component descriptor
and sets up the defined component so that it integrates with the rest of the
page.
Data System
Data system allows Bake.vue to fetch data and pass it to the component. Use
Baked.Ui.Fetch builder class to create different types of data for a
component. Below you can find some example data usages.
...
// inline a static value
Fetch.Inline("a string");
Fetch.Inline(new { an: "object" });
// set a dynamic value that is fetched from an api endpoint
Fetch.Remote("some/endpoint");
// set a dynamic value fetched using a composable, `useRoute` in this case
Fetch.Computed.UseRoute("query");
// set a context value, model in this case
Fetch.Context.Model();
...
Action System
Action system allows Bake.vue to execute actions upon model change or submit
event of the component. Use Baked.Ui.Execute builder class to create
types of actions for a component. Below you can find some example action usages.
...
// publishes a page-wide event
Execute.Publish.Event("my-button-clicked");
// sets value to a page-wide context with given key
Execute.Publish.PageContextValue("my-value");
// redirects app to given route
Execute.Local.UseRedirect("/return-page");
// sends configured request to a remote endpoint
Execute.Remote("some/endpoint",
// goes to a page after remote action
postAction: Execute.Local.UseRedirect("/return-page")
);
...
Reaction System
Reaction system allows Bake.vue to react to publish actions, either event or
page context value. Available reactions are Reload and Show. Below you can
find some example action usages.
// reacts on an event
component.ReloadOn("my-button-clicked");
// reacts when a page context value changes
component.ReloadWhen("my-value");
// shows/hides component depending on the constraint
component.ShowOn("input-changed", Evaluate.Is("show"));
// shows when `selection` value is valid using a custom validator composable
// `useMyCustomValidator`
component.ShowWhen("selection",
constraint: Evaluate.Composable.Use("MyCustomValidator")
);