-
Notifications
You must be signed in to change notification settings - Fork 50
fpm-writer - Update ControllerExtension.d.ts for easier usage #3658
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
"@sap-ux/fe-fpm-writer": patch | ||
--- | ||
|
||
fpm-writer - Update ControllerExtension.d.ts for easier usage |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,38 @@ | ||
/** | ||
* Helper to be able to define how to get the page specific extension API when writing a controller extension. | ||
*/ | ||
* Helper to be able to define how to get the page specific extension API when writing a controller extension. | ||
*/ | ||
declare module 'sap/ui/core/mvc/ControllerExtension' { | ||
export default class ControllerExtension<API> { | ||
static overrides: unknown; | ||
base: { | ||
getExtensionAPI(): API; | ||
} | ||
} | ||
} | ||
import View from 'sap/ui/core/mvc/View'; | ||
import AppComponent from 'sap/fe/core/AppComponent'; | ||
import Controller from 'sap/ui/core/mvc/Controller'; | ||
import EditFlow from 'sap/fe/core/controllerextensions/EditFlow'; | ||
import IntentBasedNavigation from 'sap/fe/core/controllerextensions/IntentBasedNavigation'; | ||
import MessageHandler from 'sap/fe/core/controllerextensions/MessageHandler'; | ||
import Paginator from 'sap/fe/core/controllerextensions/Paginator'; | ||
import Recommendations from 'sap/fe/core/controllerextensions/Recommendations'; | ||
import Routing from 'sap/fe/core/controllerextensions/Routing'; | ||
import Share from 'sap/fe/core/controllerextensions/Share'; | ||
import ViewState from 'sap/fe/core/controllerextensions/ViewState'; | ||
import CoreElement from 'sap/ui/core/Element'; | ||
|
||
export default class ControllerExtension<ExtensionAPI> { | ||
override?: Overrides; | ||
base: { | ||
getExtensionAPI(): ExtensionAPI; | ||
getView(): View; | ||
getAppComponent(): AppComponent; | ||
byId(id: string): CoreElement | ||
}; | ||
} | ||
|
||
export type Overrides<GenericController extends Controller = Controller> = { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For the overrides I use import ViewState from "sap/fe/core/controllerextensions/ViewState";
import EditFlow from "sap/fe/core/controllerextensions/EditFlow";
import Routing from "sap/fe/core/controllerextensions/Routing";
import Controller from "sap/ui/core/mvc/Controller";
type ControllerExtensionOverrides = Partial<Pick<typeof Controller["prototype"], "onAfterRendering" | "onBeforeRendering" | "onExit" | "onInit">>;
/**
* Types for Fiori Elements Controller Extension Overrides (sap.ui.core.mvc.ControllerExtension.override).
*/
type FioriElementsControllerExtensionOverrides = ControllerExtensionOverrides & {
/**
* experimental
*/
onPageReady?: () => void;
editFlow?: Partial<Pick<typeof EditFlow["prototype"], "onAfterCreate" | "onAfterDelete" | "onAfterDiscard" | "onAfterEdit" | "onAfterSave" | "onBeforeCreate" | "onBeforeDelete" | "onBeforeDiscard" | "onBeforeEdit" | "onBeforeSave">>;
routing?: Partial<Pick<typeof Routing["prototype"], "onAfterBinding" | "onBeforeBinding" | "onBeforeNavigation">>;
viewState?: Partial<Pick<typeof ViewState["prototype"], "adaptBindingRefreshControls" | "adaptBindingRefreshHandler" | "adaptControlStateHandler" |"adaptStateControls" | "applyAdditionalStates" | "applyInitialStateOnly" | "applyNavigationParameters" | "onAfterStateApplied" | "onBeforeStateApplied" | "retrieveAdditionalStates">>;
}; in my controller extension. /**
* A utility type that extracts the keys of an object `T` that correspond to methods
* starting with the prefix "on".
*/
type OnMethodKeys<T> = {
[K in keyof T]: K extends `on${string}` // 1. If the key starts with "on"...
? T[K] extends (...args: any[]) => any // 2. ...and the property is a function...
? K // ...keep the key.
: never
: never; // 3. Discard all other keys.
}[keyof T]; // 4. Collect the kept keys into a union type.
//[...]
editFlow?: Partial<Pick<typeof EditFlow["prototype"], OnMethodKeys<typeof EditFlow["prototype"]>>>; @MarcelWaechter But actually I would prefer if Fiori elements would provide those types (UI5 version dependent). This would be a great feature, though I understand the JSDoc-based type generation is a hurdle 🙈 (Please note that There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yeah onPageReady is not a public one. Recommendations also not yet public. EditFlow, IntentBasedNavigation, MessageHandler, Paginator, Routing, Share and ViewState is fine. Yeah let's see how we can improve as well, also adding @nlunets . There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I further refined this. What could be handy is something like: import ViewState from "sap/fe/core/controllerextensions/ViewState";
import EditFlow from "sap/fe/core/controllerextensions/EditFlow";
import Routing from "sap/fe/core/controllerextensions/Routing";
import Controller from "sap/ui/core/mvc/Controller";
/**
* A utility type that extracts the keys of an object `T` that correspond to methods
* starting with the specified prefix `P`.
*
* @template T - The object type to extract method keys from
* @template P - The string prefix to match (e.g., "on", "adapt", "apply", "retrieve")
*/
type MethodKeys<T, P extends string> = {
[K in keyof T]: K extends `${P}${string}` // 1. If the key starts with the prefix...
? T[K] extends (...args: any[]) => any // 2. ...and the property is a function...
? K // ...keep the key.
: never
: never; // 3. Discard all other keys.
}[keyof T]; // 4. Collect the kept keys into a union type.
/**
* A utility type that combines method keys with multiple prefixes.
*
* @template T - The object type to extract method keys from
* @template P - A union of string prefixes to match
*/
type MultiPrefixMethodKeys<T, P extends string> = P extends string
? MethodKeys<T, P>
: never;
/**
* Types for UI5 Controller Extension Overrides (sap.ui.core.mvc.ControllerExtension.override).
*/
type ControllerExtensionOverrides = Partial<Pick<typeof Controller["prototype"], MethodKeys<typeof Controller["prototype"], "on">>>;
/**
* Types for Fiori Elements Controller Extension Overrides (sap.ui.core.mvc.ControllerExtension.override).
*/
type FioriElementsControllerExtensionOverrides = ControllerExtensionOverrides & {
/**
* experimental
*/
onPageReady?: () => void;
editFlow?: Partial<Pick<typeof EditFlow["prototype"], MethodKeys<typeof EditFlow["prototype"], "on">>>;
routing?: Partial<Pick<typeof Routing["prototype"], MethodKeys<typeof Routing["prototype"], "on">>>;
viewState?: Partial<Pick<typeof ViewState["prototype"], MultiPrefixMethodKeys<typeof ViewState["prototype"], "on" | "adapt" | "apply" | "retrieve">>>;
}; If Fiori elements keeps following those function naming pattern this should be fine. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. not sure, the fact a method starts with on doesn't say it's public and can be overridden. That's defined in the decorators, like @publicExtension() I guess you can't read our decorators... but it's there in the metadata, just checked quickly ![]() but yes not great, it shall be ideally accessible through the TS types... |
||
editFlow?: Partial<EditFlow>; | ||
intentBasedNavigation?: Partial<IntentBasedNavigation>; | ||
messageHandler?: Partial<MessageHandler>; | ||
paginator?: Partial<Paginator>; | ||
recommendations?: Partial<Recommendations>; | ||
routing?: Partial<Routing>; | ||
share?: Partial<Share>; | ||
viewState?: Partial<ViewState>; | ||
} & Partial<GenericController>; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually I use
as type in my controller extension. I don't think you need to list all PageController functions if you just re-use that type. And it also comes with less maintenance in case the PageController type changes later on.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another thing that came into my mind. Actually base can be the actual controller that is extended (ListReportController or ObjectPageController), that itself returns the respective ExtensionAPI (LR or OP ExtensionAPI), and some additional APIs.
https://ui5.sap.com/#/api/sap.fe.templates
The ObjectPageController is missing from our public API, but I'd say we should make it public as well (as it can also be extended already) - any objections @nlunets ?