|
|
@@ -5,19 +5,17 @@ weight: 5
|
|
|
|
|
|
# Custom Form Inputs
|
|
|
|
|
|
-Another way to extend the Admin UI app is to define custom form input components for manipulating any [Custom Fields]({{< ref "/reference/typescript-api/custom-fields" >}}) you have defined on your entities as well as [configurable args]({{< relref "config-args" >}}) used by custom [ConfigurableOperationDefs]({{< relref "configurable-operation-def" >}}).
|
|
|
+Another way to extend the Admin UI app is to define custom form input components for manipulating any [Custom Fields](/guides/developer-guide/custom-fields/) you have defined on your entities as well as [configurable args](/reference/typescript-api/configurable-operation-def/config-args/) used by custom [Configurable Operations](/guides/developer-guide/strategies-configurable-operations/#configurable-operations).
|
|
|
|
|
|
## For Custom Fields
|
|
|
|
|
|
Let's say you define a custom "intensity" field on the Product entity:
|
|
|
|
|
|
-```ts
|
|
|
-// project/vendure-config.ts
|
|
|
-
|
|
|
+```ts title="src/vendure-config.ts"
|
|
|
customFields: {
|
|
|
- Product: [
|
|
|
- { name: 'intensity', type: 'int', min: 0, max: 100, defaultValue: 0 },
|
|
|
- ],
|
|
|
+ Product: [
|
|
|
+ {name: 'intensity', type: 'int', min: 0, max: 100, defaultValue: 0},
|
|
|
+ ],
|
|
|
}
|
|
|
```
|
|
|
|
|
|
@@ -25,79 +23,78 @@ By default, the "intensity" field will be displayed as a number input:
|
|
|
|
|
|

|
|
|
|
|
|
-But let's say we want to display a range slider instead. Here's how we can do this using our shared extension module combined with the [registerFormInputComponent function]({{< relref "register-form-input-component" >}}):
|
|
|
+But let's say we want to display a range slider instead. Here's how we can do this using our shared extension module combined with the [registerFormInputComponent function](/reference/admin-ui-api/custom-input-components/register-form-input-component/):
|
|
|
|
|
|
-```ts
|
|
|
-// project/ui-extensions/shared.module.ts
|
|
|
+```ts title="src/ui-extensions/slider-form-input.component.ts"
|
|
|
import { NgModule, Component } from '@angular/core';
|
|
|
import { FormControl } from '@angular/forms';
|
|
|
-import { IntCustomFieldConfig ,SharedModule, FormInputComponent, registerFormInputComponent } from '@vendure/admin-ui/core';
|
|
|
+import { IntCustomFieldConfig, SharedModule, FormInputComponent, registerFormInputComponent } from '@vendure/admin-ui/core';
|
|
|
|
|
|
@Component({
|
|
|
- template: `
|
|
|
- <input
|
|
|
- type="range"
|
|
|
- [min]="config.min || 0"
|
|
|
- [max]="config.max || 100"
|
|
|
- [formControl]="formControl" />
|
|
|
- {{ formControl.value }}
|
|
|
+ template: `
|
|
|
+ <input
|
|
|
+ type="range"
|
|
|
+ [min]="config.min || 0"
|
|
|
+ [max]="config.max || 100"
|
|
|
+ [formControl]="formControl" />
|
|
|
+ {{ formControl.value }}
|
|
|
`,
|
|
|
})
|
|
|
export class SliderControl implements FormInputComponent<IntCustomFieldConfig> {
|
|
|
- readonly: boolean;
|
|
|
- config: IntCustomFieldConfig;
|
|
|
- formControl: FormControl;
|
|
|
+ readonly: boolean;
|
|
|
+ config: IntCustomFieldConfig;
|
|
|
+ formControl: FormControl;
|
|
|
}
|
|
|
|
|
|
@NgModule({
|
|
|
- imports: [SharedModule],
|
|
|
- declarations: [SliderControl],
|
|
|
- providers: [
|
|
|
- registerFormInputComponent('slider-form-input', SliderControl),
|
|
|
- ]
|
|
|
+ imports: [SharedModule],
|
|
|
+ declarations: [SliderControl],
|
|
|
+ providers: [
|
|
|
+ registerFormInputComponent('slider-form-input', SliderControl),
|
|
|
+ ]
|
|
|
})
|
|
|
-export class SharedExtensionModule {}
|
|
|
+export class SharedExtensionModule {
|
|
|
+}
|
|
|
```
|
|
|
-The `SharedExtensionModule` is then passed to the `compileUiExtensions()` function as described in the [UI Extensions With Angular guide]({{< relref "using-angular" >}}#4-pass-the-extension-to-the-compileuiextensions-function):
|
|
|
|
|
|
-```ts
|
|
|
-// project/vendure-config.ts
|
|
|
+The `SharedExtensionModule` is then passed to the `compileUiExtensions()` function as described in the [UI Extensions With Angular guide](/guides/extending-the-admin-ui/using-angular/#4-pass-the-extension-to-the-compileuiextensions-function):
|
|
|
+
|
|
|
+```ts title="src/vendure-config.ts"
|
|
|
AdminUiPlugin.init({
|
|
|
- port: 5001,
|
|
|
- app: compileUiExtensions({
|
|
|
- outputPath: path.join(__dirname, '../admin-ui'),
|
|
|
- extensions: [{
|
|
|
- extensionPath: path.join(__dirname, 'ui-extensions'),
|
|
|
- ngModules: [{
|
|
|
- type: 'shared',
|
|
|
- ngModuleFileName: 'shared.module.ts',
|
|
|
- ngModuleName: 'SharedExtensionModule',
|
|
|
- }],
|
|
|
- }],
|
|
|
- }),
|
|
|
+ port: 5001,
|
|
|
+ app: compileUiExtensions({
|
|
|
+ outputPath: path.join(__dirname, '../admin-ui'),
|
|
|
+ extensions: [{
|
|
|
+ extensionPath: path.join(__dirname, 'ui-extensions'),
|
|
|
+ ngModules: [{
|
|
|
+ type: 'shared',
|
|
|
+ ngModuleFileName: 'shared.module.ts',
|
|
|
+ ngModuleName: 'SharedExtensionModule',
|
|
|
+ }],
|
|
|
+ }],
|
|
|
+ }),
|
|
|
})
|
|
|
```
|
|
|
|
|
|
Once registered, this new slider input can be used in our custom field config:
|
|
|
|
|
|
-```ts {hl_lines=[7]}
|
|
|
-// project/vendure-config.ts
|
|
|
-
|
|
|
+```ts title="src/vendure-config.ts"
|
|
|
customFields: {
|
|
|
- Product: [
|
|
|
- {
|
|
|
- name: 'intensity', type: 'int', min: 0, max: 100, defaultValue: 0,
|
|
|
- ui: { component: 'slider-form-input' }
|
|
|
- },
|
|
|
- ],
|
|
|
+ Product: [
|
|
|
+ {
|
|
|
+ name: 'intensity', type: 'int', min: 0, max: 100, defaultValue: 0,
|
|
|
+ // highlight-next-line
|
|
|
+ ui: {component: 'slider-form-input'}
|
|
|
+ },
|
|
|
+ ],
|
|
|
}
|
|
|
```
|
|
|
As we can see, adding the `ui` property to the custom field config allows us to specify our custom slider component.
|
|
|
The component id _'slider-form-input'_ **must match** the string passed as the first argument to `registerFormInputComponent()`.
|
|
|
|
|
|
-{{% alert %}}
|
|
|
+:::info
|
|
|
If we want, we can also pass any other arbitrary data in the `ui` object, which will then be available in our component as `this.config.ui.myField`. Note that only JSON-compatible data types are permitted, so no functions or class instances.
|
|
|
-{{< /alert >}}
|
|
|
+:::
|
|
|
|
|
|
|
|
|
Re-compiling the Admin UI will result in our SliderControl now being used for the "intensity" custom field:
|
|
|
@@ -120,7 +117,7 @@ If you have a custom field of the `relation` type (which allows you to relate en
|
|
|
|
|
|
Here is a simple example taken from the [real-world-vendure](https://github.com/vendure-ecommerce/real-world-vendure/blob/master/src/plugins/reviews/ui/components/featured-review-selector/featured-review-selector.component.ts) repo:
|
|
|
|
|
|
-```ts
|
|
|
+```ts title="src/plugins/reviews/ui/components/relation-review-input/relation-review-input.component.ts"
|
|
|
import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
|
|
|
import { FormControl } from '@angular/forms';
|
|
|
import { ActivatedRoute } from '@angular/router';
|
|
|
@@ -181,27 +178,29 @@ export class RelationReviewInputComponent implements OnInit, FormInputComponent<
|
|
|
|
|
|
## For ConfigArgs
|
|
|
|
|
|
-[ConfigArgs]({{< relref "config-args" >}}) are used by classes which extend [ConfigurableOperationDef]({{< relref "configurable-operation-def" >}}) (such as ShippingCalculator or PaymentMethodHandler). These ConfigArgs allow user-input values to be passed to the operation's business logic.
|
|
|
+[ConfigArgs](/reference/typescript-api/configurable-operation-def/config-args/) are used by classes which extend [Configurable Operations](/guides/developer-guide/strategies-configurable-operations/#configurable-operations) (such as ShippingCalculator or PaymentMethodHandler). These ConfigArgs allow user-input values to be passed to the operation's business logic.
|
|
|
|
|
|
They are configured in a very similar way to custom fields, and likewise can use custom form inputs by specifying the `ui` property.
|
|
|
|
|
|
Here's an example:
|
|
|
|
|
|
-```ts {hl_lines=[6,7,8]}
|
|
|
+```ts title="src/config/order-fixed-discount-action.ts"
|
|
|
export const orderFixedDiscount = new PromotionOrderAction({
|
|
|
- code: 'order_fixed_discount',
|
|
|
- args: {
|
|
|
- discount: {
|
|
|
- type: 'int',
|
|
|
- ui: {
|
|
|
- component: 'currency-form-input',
|
|
|
- },
|
|
|
+ code: 'order_fixed_discount',
|
|
|
+ args: {
|
|
|
+ discount: {
|
|
|
+ type: 'int',
|
|
|
+ // highlight-start
|
|
|
+ ui: {
|
|
|
+ component: 'currency-form-input',
|
|
|
+ },
|
|
|
+ // highlight-end
|
|
|
+ },
|
|
|
+ },
|
|
|
+ execute(ctx, order, args) {
|
|
|
+ return -args.discount;
|
|
|
},
|
|
|
- },
|
|
|
- execute(ctx, order, args) {
|
|
|
- return -args.discount;
|
|
|
- },
|
|
|
- description: [{ languageCode: LanguageCode.en, value: 'Discount order by fixed amount' }],
|
|
|
+ description: [{languageCode: LanguageCode.en, value: 'Discount order by fixed amount'}],
|
|
|
});
|
|
|
```
|
|
|
|