Vue Model Layer
The model layer, generated as models.g.ts
, contains a set of TypeScript interfaces that represent each client-exposed type in your data model. Each interface contains all the Properties of that type, as well as a $metadata
property that references the metadata object for that type. Enums and Data Sources are also represented in the model layer.
The model layer also includes a TypeScript class for each type that can be used to easily instantiate a valid implementation of its corresponding interface. However, it is not necessary for the classes to be used, and all parts of Coalesce that interact with the model layer don't perform any instanceof
checks against models - the $metadata
property is used to determine type identity.
Concepts
The model layer is fairly simple - the only main concept it introduces on top of the Metadata Layer is the notion of interfaces and enums that mirror the C# types in your data model. As with the Metadata Layer, the source code of coalesce-vue is a great documentation supplement to this page.
Model
An interface describing an instance of a class type from your application's data model. All Model interfaces contain members for all the Properties of that type, as well as a $metadata
property that references the metadata object for that type.
DataSource
A class-based representation of a Data Source containing properties for any of the Custom Parameters of the data source, as well as a $metadata
property that references the metadata object for the data source.
Data sources are generated as concrete classes in a namespace named DataSources
that is nested inside a namespace named after their parent model type. For example:
import { Person } from '@/models.g'
const dataSource = new Person.DataSources.NamesStartingWith;
dataSource.startsWith = "A";
// Provide the dataSource to an API Client or a ViewModel...
Model Functions
The following functions exported from coalesce-vue
can be used with your models:
// Vue Options API
bindToQueryString: {
(vue: Vue, obj: {}, key: string, options?: BindToQueryStringOptions);
(vue: Vue, ref: Ref<any>, queryKey: string);
(vue: Vue, ref: Ref<any>, options: BindToQueryStringOptions);
}
// Vue Composition API
useBindToQueryString: {
(obj: {}, key: string, options?: BindToQueryStringOptions);
(ref: Ref<any>, queryKey: string);
(ref: Ref<any>, options: BindToQueryStringOptions);
}
// Vue Options API
bindToQueryString: {
(vue: Vue, obj: {}, key: string, options?: BindToQueryStringOptions);
(vue: Vue, ref: Ref<any>, queryKey: string);
(vue: Vue, ref: Ref<any>, options: BindToQueryStringOptions);
}
// Vue Composition API
useBindToQueryString: {
(obj: {}, key: string, options?: BindToQueryStringOptions);
(ref: Ref<any>, queryKey: string);
(ref: Ref<any>, options: BindToQueryStringOptions);
}
export interface BindToQueryStringOptions<TValue> {
/** The key in the query string that holds the bound value. */
queryKey?: string;
/** Convert the query string value to the model value. */
parse?: (v: string) => TValue;
/** Convert the bound value to a string representation to store in the query string. */
stringify?: (v: NonNullable<TValue>) => string | null | undefined;
/** Controls whether changes are pushed as new history state entries, or replace the current history entry. */
mode?: "push" | "replace";
}
Binds a value on an object, or the value of a ref, to the query string. When the object's value changes, the query string will be updated using vue-router. When the query string changes, the object's value will be updated.
For example:
import { useBindToQueryString } from 'coalesce-vue';
// Bind pagination information to the query string:
const list = new PersonListViewModel();
useBindToQueryString(list.$params, 'pageSize', { parse: parseInt });
const activeTab = ref("1")
useBindToQueryString(activeTab, 'activeTab');
The query string will be updated using either `router.push` or `router.replace` depending on the value of parameter `mode`.
If the query string contains a value when this is called, the object will be updated with that value immediately.
If the object being bound to has $metadata
, information from that metadata will be used to serialize and parse values to and from the query string. Otherwise, the stringify
option (default: String(value)
) will be used to serialize the value, and the parse
option (if provided) will be used to parse the value from the query string.
bindKeyToRouteOnCreate(vue: Vue, model: Model<ModelType>, routeParamName: string = 'id', keepQuery: boolean = false)
useBindKeyToRouteOnCreate(model: Model<ModelType>, routeParamName: string = 'id', keepQuery: boolean = false)
bindKeyToRouteOnCreate(vue: Vue, model: Model<ModelType>, routeParamName: string = 'id', keepQuery: boolean = false)
useBindKeyToRouteOnCreate(model: Model<ModelType>, routeParamName: string = 'id', keepQuery: boolean = false)
When model
is created (i.e. its primary key becomes non-null), replace the current URL with one that includes uses primary key for the route parameter named by routeParamName
.
The query string will not be kept when the route is changed unless true
is given to keepQuery
.
import { useBindKeyToRouteOnCreate } from 'coalesce-vue';
const props = defineProps<{id: number}>();
const viewModel = new PersonViewModel();
if (props.id) {
viewModel.$load(props.id);
} else {
useBindToQueryString(viewModel);
}
Note
The route will be replaced directly via the HTML5 History API such that vue-router will not observe the change as an actual route change, preventing the current view from being recreated if a path-based key is being used on the application's <router-view>
component.
Advanced Model Functions
The following functions exported from coalesce-vue
can be used with your models.
Note
These functions are used to implement the higher-order layers in the Vue stack.
While you're absolutely free to use them in your own code and can rely on their interface and behavior to remain consistent, you will find that you seldom need to use them directly - that's why we've split them into their own section here in the documentation.
convertToModel(value: any, metadata: Value | ClassType): any
convertToModel(value: any, metadata: Value | ClassType): any
Given any JavaScript value value
, convert it into a valid implementation of the value or type described by metadata
.
For metadata describing a primitive or primitive-like value, the input will be parsed into a valid implementation of the correct JavaScript type. For example, for metadata
that describes a boolean, a string "true"
will return a boolean true
, and ISO 8601 date strings will result in a JavaScript Date
object.
For metadata describing a type, the input object will be mutated into a valid implementation of the appropriate model interface. Missing properties will be set to null, and any descendent properties of the provided object will be recursively processed with convertToModel
.
If any values are encountered that are fundamentally incompatible with the requested type described by the metadata, an error will be thrown.
mapToModel(value: any, metadata: Value | ClassType): any
mapToModel(value: any, metadata: Value | ClassType): any
Performs the same operations as convertToModel
, except that any objects encountered will not be mutated - instead, a new object or array will always be created.
mapToDto(value: any, metadata: Value | ClassType): any
mapToDto(value: any, metadata: Value | ClassType): any
Maps the input to a representation suitable for JSON serialization.
Will not serialize child objects or collections whose metadata includes dontSerialize
. Will only recurse to a maximum depth of 3.
modelDisplay(model: Model, options?: DisplayOptions): string
modelDisplay(model: Model, options?: DisplayOptions): string
Returns a string representing the model
suitable for display in a user interface.
Uses the displayProp
defined on the object's metadata. If no displayProp
is defined, the object will be displayed as JSON. The display prop on a model can be defined in C# with [ListText].
See DisplayOptions for available options.
propDisplay(model: Model, prop: Property | string, options?: DisplayOptions): string
propDisplay(model: Model, prop: Property | string, options?: DisplayOptions): string
Returns a string representing the specified property of the given object suitable for display in a user interface.
The property can either be a string, representing one of the model's properties, or the actual Property
metadata object of the property.
See DisplayOptions for available options.
valueDisplay(value: any, metadata: Value, options?: DisplayOptions): string
valueDisplay(value: any, metadata: Value, options?: DisplayOptions): string
Returns a string representing the given value (described by the given metadata).
See DisplayOptions for available options.
DisplayOptions
The following options are available to functions in coalesce-vue that render a value or object for display:
export interface DisplayOptions {
/** Date format options. One of:
* - A UTS#35 date format string (https://date-fns.org/docs/format)
* - An object with options for https://date-fns.org/docs/format or https://github.com/marnusw/date-fns-tz#format, including a string `format` for the format itself. If a `timeZone` option is provided per https://github.com/marnusw/date-fns-tz#format, the date being formatted will be converted to that timezone.
* - An object with options for https://date-fns.org/docs/formatDistance */
format?:
| string
| ({
/** A UTS#35 date format string (https://date-fns.org/docs/format) */
format: string;
} & Parameters<typeof format>[2])
| {
/** Format date with https://date-fns.org/docs/formatDistanceToNow */
distance: true;
/** Append/prepend `'in'` or `'ago'` if date is after/before now. Default `true`. */
addSuffix?: boolean;
/** Include detail smaller than one minute. Default `false`. */
includeSeconds?: boolean;
};
collection?: {
/** The maximum number of items to display individually.
* When there are more than this number of items, the count of items will be displayed instead.
* Default `5`.
* */
enumeratedItemsMax?: number;
/** The separator to place between enumerated items. Default `', '` */
enumeratedItemsSeparator?: string;
};
}
Note
Dates rendered with the formatDistanceToNow
function into a Vue component will not automatically be updated in realtime. If this is needed, you should use a strategy like using a key that you periodically update to force a re-render.
Time Zones
In Coalesce Vue, all DateTimeOffset
-based properties, for both inputs and display-only contexts, are by default formatted into the user's computer's system time zone. This is largely just a consequence of how the JavaScript Date type works. However, this behavior can be overridden by configuring a global default timezone, or by providing a time zone name to individual usages.
Fields with a type of DateTime
are agnostic to time zone and UTC offset and so are not subject to any of the following rules.
setDefaultTimeZone(timeZoneName: string | null): void
setDefaultTimeZone(timeZoneName: string | null): void
Gets or sets the default time zone used by Coalesce. The time zone should be an IANA Time Zone Database name, e.g. "America/Los_Angeles"
.
The time zone provided here is used in the following ways:
- It will be used as
DisplayOptions.format.timeZone
if no other value was provided for this option. This is used by functions modelDisplay, propDisplay, and valueDisplay, as well as the c-display component. - It will be used by c-datetime-picker, used to both interpret the user input and display the selected date. This can also be set on individual component usages via the
timeZone
prop. - It will be used when serializing DateTimeOffset fields into JSON DTOs, representing the ISO 8601 date string in the specified time zone rather than in the user's computer's system time zone.
getDefaultTimeZone(): string | null
getDefaultTimeZone(): string | null
Returns the current configured default time zone. Default is null
, falling back on the user's computer's system time zone.