import type { ModelType, PropertyOrName, PropNames, ModelCollectionNavigationProperty, ModelCollectionValue, Service, Property } from "./metadata.js"; import { ModelApiClient, ListParameters, DataSourceParameters, ServiceApiClient, type BulkSaveRequestItem, ItemApiState } from "./api-client.js"; import { type Model, type DisplayOptions } from "./model.js"; import { type DeepPartial, type VueInstance } from "./util.js"; import { debounce } from "lodash-es"; export type { DeepPartial } from "./util.js"; import type * as apiClient from "./api-client.js"; /** * * If true, the data is clean with no known age. * * If false, the data is dirty. * * If number, the timestamp indicating the date of the data. */ type DataFreshness = boolean | number; export declare abstract class ViewModel = any, TApi extends ModelApiClient = any, TPrimaryKey extends string | number | Date = any> implements Model { /** Instance of an API client for the model through which direct, stateless API requests may be made. */ readonly $apiClient: TApi; /** Static lookup of all generated ViewModel types. */ static typeLookup: ViewModelTypeLookup | null; /** * A number unique among all ViewModel instances that will be unchanged for the instance's lifetime. * Can be used to uniquely identify objects with `:key` in Vue components. * * Especially useful with `transition-group` where some elements of iteration may be unsaved * and therefore not yet have a `$primaryKey`. */ readonly $stableId: number; /** * Gets or sets the primary key of the ViewModel's data. */ get $primaryKey(): TPrimaryKey; set $primaryKey(val: TPrimaryKey); /** * Returns true if the values of the savable data properties of this ViewModel * have changed since the last load, save, or the last time $isDirty was set to false. */ get $isDirty(): boolean; set $isDirty(val: boolean); $setPropDirty(propName: PropNames, dirty?: boolean, triggerAutosave?: boolean): void; $getPropDirty(propName: PropNames): boolean; /** The parameters that will be passed to `/get`, `/save`, and `/delete` calls. */ get $params(): DataSourceParameters; set $params(val: DataSourceParameters); /** Wrapper for `$params.dataSource` */ get $dataSource(): DataSourceParameters["dataSource"]; set $dataSource(val: DataSourceParameters["dataSource"]); /** Wrapper for `$params.includes` */ get $includes(): string | null | undefined; set $includes(val: string | null | undefined); $addRule(prop: string | Property, identifier: string, rule: (val: any) => true | string): void; $removeRule(prop: string | Property, identifier: string): void; /** Returns an array of all applicable rules for the given property in accordance with: - The rules defined for the model's properties' metadata, - Custom rules added by calling `$addRule` - Any rules that where ignored by calling `this.$removeRule`,*/ $getRules(prop: string | Property): (((val: any) => true | string) & { ruleName: string; })[] | undefined; /** Returns an array of all error messages for the current model in accordance with: - The rules defined for the model's properties' metadata, - Custom rules added by calling `$addRule` - Any rules that where ignored by calling `this.$removeRule`, */ $getErrors(prop?: string | Property): string[]; /** True if `this.$getErrors()` is returning at least one error. */ get $hasError(): boolean; /** * A function for invoking the `/get` endpoint, and a set of properties about the state of the last call. */ get $load(): ItemApiState<[id?: TPrimaryKey | undefined], TModel, import("./metadata.js").ItemMethod>; /** Whether or not to reload the ViewModel's `$data` with the response received from the server after a call to .$save(). */ $loadResponseFromSaves: boolean; /** Whether to save the whole object, or only properties which are dirty. * - `surgical` - save only properties that are flagged as dirty (default). * - `whole` - save all properties on the object. */ $saveMode: "surgical" | "whole"; /** When `$save.isLoading == true`, contains the properties of the model currently being saved by `$save` (including autosaves). * * Does not include non-dirty properties even if `$saveMode == 'whole'`. */ get $savingProps(): ReadonlySet; set $savingProps(value: ReadonlySet); /** * A function for invoking the `/save` endpoint, and a set of properties about the state of the last call. */ get $save(): ItemApiState<[overrideProps?: Partial | undefined], TModel, import("./metadata.js").ItemMethod>; /** * A function for invoking the `/bulkSave` endpoint, and a set of properties about the state of the last call. * * A bulk save will save/delete this entity and all reachable relationships in a single round trip and database transaction. */ get $bulkSave(): ItemApiState<[options?: BulkSaveOptions | undefined], TModel, import("./metadata.js").ItemMethod>; /** Returns the payload that will be used for the `$bulkSave` operation. * * Useful for driving UI state like preemptively showing errors, * or determining if there are any objects with pending modifications. */ $bulkSavePreview(options?: BulkSaveOptions): { /** True if there are any models with a pending save or delete. */ isDirty: boolean; /** The actual data to be sent in the API request. */ items: BulkSaveRequestItem[]; /** Full representation of the items that will be sent in the bulk save request. */ rawItems: BulkSaveRequestRawItem[]; /** Aggregation of all errors contained within `rawItems`. */ errors: string[]; /** @internal */ _allItems: Map; }; /** * Loads data from the provided model into the current ViewModel, * and then clears the $isDirty flag if `isCleanData` is not given as `false`. * * Data is loaded in a surgical fashion that will preserve existing instances * of objects and arrays found on navigation properties. */ $loadFromModel(source: DeepPartial, isCleanData?: DataFreshness, purgeUnsaved?: boolean): this; /** * Loads data from the provided model into the current ViewModel, and then clears $isDirty flags. * * Data is loaded in a surgical fashion that will preserve existing instances * of objects and arrays found on navigation properties. */ $loadCleanData(source: DeepPartial, purgeUnsaved?: boolean): this; /** * Loads data from the provided model into the current ViewModel. * Does not clear $isDirty flags. * * Data is loaded in a surgical fashion that will preserve existing instances * of objects and arrays found on navigation properties. */ $loadDirtyData(source: DeepPartial): this; /** * A function for invoking the `/delete` endpoint, and a set of properties about the state of the last call. */ get $delete(): ItemApiState<[], TModel, import("./metadata.js").ItemMethod>; get $isRemoved(): boolean; /** Mark this model for deletion in the next `$bulkSave` operation performed on an ancestor. * * If this item has a parent collection, it will be immediately removed from that collection. * */ $remove(): void; /** * Starts auto-saving of the instance when changes to its savable data properties occur. * Only usable from Vue setup() or `script setup`. Otherwise, use $startAutoSave(). * @param options Options to control how the auto-saving is performed. */ $useAutoSave(options?: AutoSaveOptions): void; /** * Starts auto-saving of the instance when changes to its savable data properties occur. * @param vue A Vue instance through which the lifecycle of the watcher will be managed. * @param options Options to control how the auto-saving is performed. */ $startAutoSave(vue: VueInstance, options?: AutoSaveOptions): void; /** Stops auto-saving if it is currently enabled. */ $stopAutoSave(): void; /** Returns true if auto-saving is currently enabled. */ get $isAutoSaveEnabled(): boolean | undefined; /** * Returns a string representation of the object, or one of its properties, suitable for display. * @param prop If provided, specifies a property whose value will be displayed. * @param options Options for formatting the displayed value. * If omitted, the whole object will be represented. */ $display(prop?: PropertyOrName, options?: DisplayOptions): string | null; /** * Creates a new instance of an item for the specified child model collection, * adds it to that collection, and returns the item. * @param prop The name of the collection property, or the metadata representing it. */ $addChild(prop: ModelCollectionNavigationProperty | PropNames, initialDirtyData?: any): ViewModel; /** The metadata representing the type of data that this ViewModel handles. */ readonly $metadata: TModel["$metadata"]; constructor($metadata: TModel["$metadata"], /** Instance of an API client for the model through which direct, stateless API requests may be made. */ $apiClient: TApi, initialDirtyData?: DeepPartial | null); } /** Create a class that represents an abstract model type * and will turn itself into the proper concrete ViewModel type * when loaded. */ export declare function createAbstractProxyViewModelType, TViewModel extends ViewModel>(metadata: ModelType, apiClientCtor: { new (): ModelApiClient; }): { new (initialData?: DeepPartial | null): TViewModel; }; export interface BulkSaveRequestRawItem { model: ViewModel; metadata: ModelType; action: BulkSaveRequestItem["action"]; refs: BulkSaveRequestItem["refs"] & {}; isRoot: boolean; errors?: string[]; } export declare abstract class ListViewModel = any, TApi extends ModelApiClient = ModelApiClient, TItem extends ViewModel = ViewModel> { /** The metadata representing the type of data that this ViewModel handles. */ readonly $metadata: TModel["$metadata"]; /** Instance of an API client for the model through which direct, stateless API requests may be made. */ readonly $apiClient: TApi; /** Static lookup of all generated ListViewModel types. */ static typeLookup: ListViewModelTypeLookup | null; /** The parameters that will be passed to `/list` and `/count` calls. */ get $params(): ListParameters; set $params(val: ListParameters); /** Wrapper for `$params.dataSource` */ get $dataSource(): ListParameters["dataSource"]; set $dataSource(val: ListParameters["dataSource"]); /** Wrapper for `$params.includes` */ get $includes(): string | null | undefined; set $includes(val: string | null | undefined); /** Wrapper for `$params.search` */ get $search(): string | null | undefined; set $search(val: string | null | undefined); /** Wrapper for `$params.filter`. * * A collection of key-value pairs to filter by, * where keys are property names on the type and values are as follows: * - Strings will match exactly unless an asterisk is found in the filter, in which case they will be matched with `string.StartsWith` with the asterisk stripped out. - Numeric values will match exactly. Multiple comma-delimited values will create a filter that will match on any of the provided values. - Enums will match by string or numeric value. Multiple comma-delimited values will create a filter that will match on any of the provided values. - Dates with a time component will be matched exactly. - Dates with no time component will match any dates that fell on that day. - The values `null` and `"null"` match a `null` property value (except string properties). */ get $filter(): { [fieldName: string]: string | number | boolean | null | undefined; }; set $filter(val: { [fieldName: string]: string | number | boolean | null | undefined; }); /** Wrapper for `$params.orderBy` */ get $orderBy(): string | null | undefined; set $orderBy(val: string | null | undefined); /** Wrapper for `$params.orderByDescending` */ get $orderByDescending(): string | null | undefined; set $orderByDescending(val: string | null | undefined); /** * Toggles the orderBy field through three states: ascending, descending, and none. * - If the field is not currently the orderBy field, it becomes the ascending orderBy field. * - If the field is the ascending orderBy field, it becomes the descending orderBy field. * - If the field is the descending orderBy field, orderBy is cleared. */ $orderByToggle(field: PropNames): void; /** The plain model items that have been loaded into this ListViewModel. * These instances do not reflect any changes made to the contents of `$items`. */ get $modelItems(): TModel[]; /** Returns true if `$modelOnlyMode` has been enabled. */ get $modelOnlyMode(): boolean; /** * Put the ListViewModel into a lightweight mode where `$items` is not populated with ViewModel instances. * Result can instead be read from `$modelItems`. * * This mode allows much better performance when loading large numbers of items, especially in read-only contexts. */ set $modelOnlyMode(val: boolean); get $items(): ViewModelCollection; set $items(val: TItem[]); /** True if the page set in $params.page is greater than 1 */ get $hasPreviousPage(): boolean; /** True if the count retrieved from the last load indicates that there may be pages after the page set in $params.page */ get $hasNextPage(): boolean; /** Decrement the page parameter by 1 if there is a previous page. */ $previousPage(): void; /** Increment the page parameter by 1 if there is a next page. */ $nextPage(): void; /** Wrapper for `$params.page` */ get $page(): number; set $page(val: number); /** Wrapper for `$params.pageSize` */ get $pageSize(): number; set $pageSize(val: number); /** The number of pages returned by the last successful load. */ get $pageCount(): number | null; /** * A function for invoking the `/load` endpoint, and a set of properties about the state of the last call. */ get $load(): apiClient.ListApiState<[], TModel, import("./metadata.js").ListMethod>; /** * A function for invoking the `/count` endpoint, and a set of properties about the state of the last call. */ get $count(): ItemApiState<[], number, import("./metadata.js").ItemMethod>; /** * Starts auto-loading of the list as changes to its parameters occur. * Only usable from Vue setup() or `script setup`. Otherwise, use $startAutoLoad(). * @param options Options that control the auto-load behavior. */ $useAutoLoad(options?: AutoLoadOptions): void; /** * Starts auto-loading of the list as changes to its parameters occur. * @param vue A Vue instance through which the lifecycle of the watcher will be managed. * @param options Options that control the auto-load behavior. */ $startAutoLoad(vue: VueInstance, options?: AutoLoadOptions): void; /** Stops auto-loading if it is currently enabled. */ $stopAutoLoad(): void; /** * Enables auto save for the items in the list. * Only usable from Vue setup() or `script setup`. Otherwise, use $startAutoSave(). * @param options Options to control how the auto-saving is performed. */ $useAutoSave(options?: AutoSaveOptions): void; /** * Enables auto save for the items in the list. * @param vue A Vue instance through which the lifecycle of the watcher will be managed. * @param options Options to control how the auto-saving is performed. */ $startAutoSave(vue: VueInstance, options?: AutoSaveOptions): void; /** Stops auto-saving if it is currently enabled. */ $stopAutoSave(): void; /** Returns true if auto-saving is currently enabled. */ get $isAutoSaveEnabled(): boolean; constructor( /** The metadata representing the type of data that this ViewModel handles. */ $metadata: TModel["$metadata"], /** Instance of an API client for the model through which direct, stateless API requests may be made. */ $apiClient: TApi); } export declare class ServiceViewModel = ServiceApiClient> { /** The metadata representing the type of data that this ViewModel handles. */ readonly $metadata: TMeta; /** Instance of an API client for the model through which direct, stateless API requests may be made. */ readonly $apiClient: TApi; /** Static lookup of all generated ServiceViewModel types. */ static typeLookup: ServiceViewModelTypeLookup | null; constructor( /** The metadata representing the type of data that this ViewModel handles. */ $metadata: TMeta, /** Instance of an API client for the model through which direct, stateless API requests may be made. */ $apiClient: TApi); } /** Factory for creating new ViewModels from some initial data. * * For all ViewModels created recursively as a result of creating the root ViewModel, * the same ViewModel instance will be returned whenever the exact same `initialData` object is encountered. */ export declare class ViewModelFactory { private isCleanData; private static current; private map; /** Ask the factory for a ViewModel for the given type and initial data. * The instance may be a brand new one, or may be already existing * if the same initialData has already been seen. */ get(typeName: string, initialData: any): ViewModel; /** Provide a pre-existing instance to the factory. */ set(initialData: any, vm: ViewModel): void; private processed; /** Determine if the given initialData was already mapped to `vm`. */ shouldProcess(initialData: any, vm: ViewModel): boolean; static scope(action: (factory: ViewModelFactory) => TRet, isCleanData: DataFreshness): TRet; static get(typeName: string, initialData: any, isCleanData: DataFreshness): ViewModel; private constructor(); } export declare class ViewModelCollection extends Array { readonly $metadata: ModelCollectionValue | ModelCollectionNavigationProperty | ModelType; readonly $parent: ViewModel | ListViewModel; $hasLoaded: boolean; push(...items: (T | Partial)[]): number; splice(start: number, deleteCount?: number, ...items: T[]): T[]; constructor($metadata: ModelCollectionValue | ModelType, $parent: ViewModel | ListViewModel); } type DebounceOptions = { /** Milliseconds to delay after a change is detected. Passed as the second parameter to lodash's `debounce` function. */ wait?: number; /** Additional options to pass to the third parameter of lodash's `debounce` function. */ debounce?: Parameters[2]; }; type AutoLoadOptions = DebounceOptions & { /** A function that will be called before autoloading that can return false to prevent a load. */ predicate?: (viewModel: TThis) => boolean; /** If true, an immediate initial load of the list will be performed. Otherwise, the initial auto-load of the list won't occur until the first change to its parameters occur. */ immediate?: boolean; }; type AutoSaveOptions = DebounceOptions & ({ /** A function that will be called before autosaving that can return false to prevent a save. */ predicate?: (viewModel: TThis) => boolean; /** If true, auto-saving will also be enabled for all view models that are * reachable from the navigation properties & collections of the current view model. */ deep?: false; } | { /** A function that will be called before autosaving that can return false to prevent a save. */ predicate?: (viewModel: ViewModel) => boolean; /** If true, auto-saving will also be enabled for all view models that are * reachable from the navigation properties & collections of the current view model. */ deep: true; }); export interface BulkSaveOptions { /** A predicate that will be applied to each modified model * to determine if it should be included in the bulk save operation. * * The predicate is applied before validation (`$hasError`), allowing * it to be used to skip over entities that have client validation errors * that would otherwise cause the entire bulk save operation to fail. * */ predicate?: (viewModel: ViewModel, action: "save" | "delete") => boolean; /** Additional root items that will be traversed for items that need saving. * Use to add items that aren't attached to the target of the bulk save, * but are still desired to be saved during the same operation. */ additionalRoots?: ViewModel[]; } /** * Dynamically adds getter/setter properties to a class. These properties wrap the properties in its instances' $data objects. * @param ctor The class to add wrapper properties to * @param metadata The metadata describing the properties to add. */ export declare function defineProps ViewModel>(ctor: T, metadata: ModelType): void; export interface ViewModelTypeLookup { [name: string]: new (initialData?: any) => ViewModel; } export interface ListViewModelTypeLookup { [name: string]: new () => ListViewModel; } export interface ServiceViewModelTypeLookup { [name: string]: new () => ServiceViewModel; } export type ModelOf = T extends ViewModel ? TModel : never; /** * Updates the target model with values from the source model. * @param target The viewmodel to be updated. * @param source The model whose values will be used to perform the update. * @param skipDirty If true, only non-dirty props, and related objects, will be updated. * Basic properties on target that are dirty will be skipped. * @param purgeUnsaved If true, unsaved items lingering in collections that are not in `source` will be removed. * @param skipStale If true, only props not actively being saved, and related objects, will be updated. * Basic properties on target that are currently being saved will be skipped. */ export declare function updateViewModelFromModel>>(target: TViewModel, source: any, skipDirty?: boolean, isCleanData?: DataFreshness, purgeUnsaved?: boolean, skipStale?: boolean): void; /** * A Vue composable that sets up two-way binding between a list view model's parameters * and the browser's query string. This captures baseline parameters and sets up * watchers to synchronize changes between the view model parameters and the router's * query parameters. * * Automatically cleans up watchers when the component is unmounted. * * Consider using `useBindToQueryString()` on individual parameters instead if you need * greater control or want to limit which parameters are bound. Since this allows control * over the data source, filters, and includes string, it can affect the data retrieved * from your server in a way that might break your application's pages. It can also * populate the `filter` parameter with unexpected data types - values pulled from the * URL will always be strings, but your code may expect numbers, bools, or other types. * This is why unlike all other ViewModel functionality, it is not exposed as a method * on the ListViewModel class. * * @param viewModel The view model whose parameters should be bound to the query string */ export declare function useBindListParametersToQueryString(viewModel: ListViewModel): () => void; declare module "@vue/reactivity" { interface RefUnwrapBailTypes { coalesceViewModels: ViewModel | ListViewModel | ServiceViewModel; } }