diff --git a/npm/ng-packs/packages/account/src/lib/account-routing.module.ts b/npm/ng-packs/packages/account/src/lib/account-routing.module.ts
index c09dede3cb..e4b18a717b 100644
--- a/npm/ng-packs/packages/account/src/lib/account-routing.module.ts
+++ b/npm/ng-packs/packages/account/src/lib/account-routing.module.ts
@@ -13,6 +13,8 @@ import { RegisterComponent } from './components/register/register.component';
import { ResetPasswordComponent } from './components/reset-password/reset-password.component';
import { eAccountComponents } from './enums/components';
import { AuthenticationFlowGuard } from './guards/authentication-flow.guard';
+import { AccountExtensionsGuard } from './guards';
+
const routes: Routes = [
{ path: '', pathMatch: 'full', redirectTo: 'login' },
@@ -68,7 +70,7 @@ const routes: Routes = [
{
path: 'manage',
component: ReplaceableRouteContainerComponent,
- canActivate: [AuthGuard],
+ canActivate: [AuthGuard, AccountExtensionsGuard],
data: {
replaceableComponent: {
key: eAccountComponents.ManageProfile,
diff --git a/npm/ng-packs/packages/account/src/lib/account.module.ts b/npm/ng-packs/packages/account/src/lib/account.module.ts
index bfa37d4bfa..26f71ef150 100644
--- a/npm/ng-packs/packages/account/src/lib/account.module.ts
+++ b/npm/ng-packs/packages/account/src/lib/account.module.ts
@@ -15,6 +15,11 @@ import { accountConfigOptionsFactory } from './utils/factory-utils';
import { AuthenticationFlowGuard } from './guards/authentication-flow.guard';
import { ForgotPasswordComponent } from './components/forgot-password/forgot-password.component';
import { ResetPasswordComponent } from './components/reset-password/reset-password.component';
+import { ExtensiblePersonalSettingsComponent } from './components/extensible-personal-settings/extensible-personal-settings.component';
+import { UiExtensionsModule } from '@abp/ng.theme.shared/extensions';
+import { ACCOUNT_EDIT_FORM_PROP_CONTRIBUTORS } from './tokens/extensions.token';
+import { AccountExtensionsGuard } from './guards/extensions.guard';
+import { HelloComponent } from './components/hello/hello.component';
const declarations = [
LoginComponent,
@@ -27,13 +32,14 @@ const declarations = [
];
@NgModule({
- declarations: [...declarations],
+ declarations: [...declarations, ExtensiblePersonalSettingsComponent, HelloComponent],
imports: [
CoreModule,
AccountRoutingModule,
ThemeSharedModule,
NgbDropdownModule,
NgxValidateCoreModule,
+ UiExtensionsModule,
],
exports: [...declarations],
})
@@ -49,6 +55,11 @@ export class AccountModule {
useFactory: accountConfigOptionsFactory,
deps: [ACCOUNT_CONFIG_OPTIONS],
},
+ {
+ provide: ACCOUNT_EDIT_FORM_PROP_CONTRIBUTORS,
+ useValue: options.editFormPropContributors,
+ },
+ AccountExtensionsGuard,
],
};
}
diff --git a/npm/ng-packs/packages/account/src/lib/components/extensible-personal-settings/extensible-personal-settings.component.html b/npm/ng-packs/packages/account/src/lib/components/extensible-personal-settings/extensible-personal-settings.component.html
new file mode 100644
index 0000000000..5b17770996
--- /dev/null
+++ b/npm/ng-packs/packages/account/src/lib/components/extensible-personal-settings/extensible-personal-settings.component.html
@@ -0,0 +1,5 @@
+
extensible-personal-settings works!
+
+
diff --git a/npm/ng-packs/packages/account/src/lib/components/extensible-personal-settings/extensible-personal-settings.component.scss b/npm/ng-packs/packages/account/src/lib/components/extensible-personal-settings/extensible-personal-settings.component.scss
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/npm/ng-packs/packages/account/src/lib/components/extensible-personal-settings/extensible-personal-settings.component.spec.ts b/npm/ng-packs/packages/account/src/lib/components/extensible-personal-settings/extensible-personal-settings.component.spec.ts
new file mode 100644
index 0000000000..e2a2102e5a
--- /dev/null
+++ b/npm/ng-packs/packages/account/src/lib/components/extensible-personal-settings/extensible-personal-settings.component.spec.ts
@@ -0,0 +1,24 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { ExtensiblePersonalSettingsComponent } from './extensible-personal-settings.component';
+
+describe('ExtensiblePersonalSettingsComponent', () => {
+ let component: ExtensiblePersonalSettingsComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ExtensiblePersonalSettingsComponent],
+ }).compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(ExtensiblePersonalSettingsComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/npm/ng-packs/packages/account/src/lib/components/extensible-personal-settings/extensible-personal-settings.component.ts b/npm/ng-packs/packages/account/src/lib/components/extensible-personal-settings/extensible-personal-settings.component.ts
new file mode 100644
index 0000000000..0b54672cab
--- /dev/null
+++ b/npm/ng-packs/packages/account/src/lib/components/extensible-personal-settings/extensible-personal-settings.component.ts
@@ -0,0 +1,42 @@
+import {Component, Injector, OnInit} from '@angular/core';
+import {EXTENSIONS_IDENTIFIER, FormPropData, generateFormFromProps} from "@abp/ng.theme.shared/extensions";
+import { eAccountComponents } from '../../enums/components';
+import {FormBuilder, FormGroup} from "@angular/forms";
+import {ProfileService} from "@abp/ng.account.core/proxy";
+
+@Component({
+ selector: 'abp-extensible-personal-settings',
+ templateUrl: './extensible-personal-settings.component.html',
+ styleUrls: ['./extensible-personal-settings.component.scss'],
+ providers:[
+ {
+ provide: EXTENSIONS_IDENTIFIER,
+ useValue: eAccountComponents.PersonalSettings,
+ },
+ ]
+})
+export class ExtensiblePersonalSettingsComponent implements OnInit {
+ selected = {a: 1};// hacky for triggering 'edit' mode of extensible-form
+
+ form: FormGroup;
+
+ constructor(
+ private profileService: ProfileService,
+ protected fb: FormBuilder,
+ protected injector: Injector,
+ ) {}
+
+
+ buildForm() {
+ const data = new FormPropData(this.injector, this.selected);
+ this.form = generateFormFromProps(data);
+ }
+
+ ngOnInit(): void {
+ this.buildForm()
+ }
+
+ save() {
+
+ }
+}
diff --git a/npm/ng-packs/packages/account/src/lib/components/hello/hello.component.ts b/npm/ng-packs/packages/account/src/lib/components/hello/hello.component.ts
new file mode 100644
index 0000000000..2fc59314aa
--- /dev/null
+++ b/npm/ng-packs/packages/account/src/lib/components/hello/hello.component.ts
@@ -0,0 +1,15 @@
+import {Component, Inject } from '@angular/core';
+import {FORM_PROP_DATA_STREAM, FormProp} from "@abp/ng.theme.shared/extensions";
+
+@Component({
+ selector: 'abp-hello',
+ template: `hello works! {{name | abpLocalization}}
`,
+ styles: [],
+})
+export class HelloComponent {
+ name:string;
+ constructor(@Inject(FORM_PROP_DATA_STREAM) private propData:FormProp) {
+ this.name = propData.displayName
+ }
+
+}
diff --git a/npm/ng-packs/packages/account/src/lib/defaults/default-personal-settings-form-props.ts b/npm/ng-packs/packages/account/src/lib/defaults/default-personal-settings-form-props.ts
new file mode 100644
index 0000000000..0624e074d3
--- /dev/null
+++ b/npm/ng-packs/packages/account/src/lib/defaults/default-personal-settings-form-props.ts
@@ -0,0 +1,15 @@
+import {ePropType, FormProp} from "@abp/ng.theme.shared/extensions";
+import {UpdateProfileDto} from "@abp/ng.account.core/proxy";
+import {Validators} from "@angular/forms";
+import {HelloComponent} from "../components/hello/hello.component";
+
+export const DEFAULT_PERSONAL_SETTINGS_UPDATE_FORM_PROPS = FormProp.createMany([
+ {
+ type: ePropType.String,
+ name: 'userName',
+ displayName: 'Account::UserName',
+ id: 'user-name',
+ validators: () => [Validators.required, Validators.maxLength(256)],
+ template: HelloComponent
+ },
+])
diff --git a/npm/ng-packs/packages/account/src/lib/guards/extensions.guard.ts b/npm/ng-packs/packages/account/src/lib/guards/extensions.guard.ts
new file mode 100644
index 0000000000..20c4269977
--- /dev/null
+++ b/npm/ng-packs/packages/account/src/lib/guards/extensions.guard.ts
@@ -0,0 +1,42 @@
+import {Injectable, Injector} from "@angular/core";
+import {CanActivate} from "@angular/router";
+import {Observable} from "rxjs";
+import {
+ ExtensionsService,
+ getObjectExtensionEntitiesFromStore,
+ mapEntitiesToContributors, mergeWithDefaultProps
+} from "@abp/ng.theme.shared/extensions";
+ import {ConfigStateService} from "@abp/ng.core";
+import {tap, map, mapTo} from "rxjs/operators";
+import {ACCOUNT_EDIT_FORM_PROP_CONTRIBUTORS, DEFAULT_ACCOUNT_FORM_PROPS} from '../tokens/extensions.token';
+import { AccountEditFormPropContributors } from '../models/config-options';
+import { eAccountComponents } from '../enums/components';
+
+@Injectable()
+export class AccountExtensionsGuard implements CanActivate {
+ constructor(private injector: Injector) {}
+
+ canActivate(): Observable {
+ const extensions: ExtensionsService = this.injector.get(ExtensionsService);
+
+ const editFormContributors: AccountEditFormPropContributors =
+ this.injector.get(ACCOUNT_EDIT_FORM_PROP_CONTRIBUTORS, null) || {};
+
+ const configState = this.injector.get(ConfigStateService);
+ return getObjectExtensionEntitiesFromStore(configState, 'Account').pipe(
+ map(entities => ({
+ [eAccountComponents.PersonalSettings]: entities.PersonalSettings,
+ })),
+ mapEntitiesToContributors(configState, 'AbpAccount'),
+ tap(objectExtensionContributors => {
+ mergeWithDefaultProps(
+ extensions.editFormProps,
+ DEFAULT_ACCOUNT_FORM_PROPS,
+ objectExtensionContributors.editForm,
+ editFormContributors,
+ );
+ }),
+ mapTo(true),
+ );
+ }
+}
diff --git a/npm/ng-packs/packages/account/src/lib/guards/index.ts b/npm/ng-packs/packages/account/src/lib/guards/index.ts
index 382170b2b1..275cd2f8e8 100644
--- a/npm/ng-packs/packages/account/src/lib/guards/index.ts
+++ b/npm/ng-packs/packages/account/src/lib/guards/index.ts
@@ -1 +1,2 @@
export * from './authentication-flow.guard';
+export * from './extensions.guard';
diff --git a/npm/ng-packs/packages/account/src/lib/models/config-options.ts b/npm/ng-packs/packages/account/src/lib/models/config-options.ts
index 898021b04e..f2e603ab32 100644
--- a/npm/ng-packs/packages/account/src/lib/models/config-options.ts
+++ b/npm/ng-packs/packages/account/src/lib/models/config-options.ts
@@ -1,3 +1,14 @@
+import {eAccountComponents} from "../enums";
+import { EditFormPropContributorCallback } from '@abp/ng.theme.shared/extensions';
+import {UpdateProfileDto} from "@abp/ng.account.core/proxy";
+
export interface AccountConfigOptions {
redirectUrl?: string;
}
+export type AccountEditFormPropContributors = Partial<{
+ [eAccountComponents.PersonalSettings]: EditFormPropContributorCallback[];
+}>;
+
+export interface AccountConfigOptions {
+ editFormPropContributors?: AccountEditFormPropContributors;
+}
diff --git a/npm/ng-packs/packages/account/src/lib/tokens/extensions.token.ts b/npm/ng-packs/packages/account/src/lib/tokens/extensions.token.ts
new file mode 100644
index 0000000000..6d95db8b6d
--- /dev/null
+++ b/npm/ng-packs/packages/account/src/lib/tokens/extensions.token.ts
@@ -0,0 +1,17 @@
+import {eAccountComponents} from "../enums";
+import {DEFAULT_PERSONAL_SETTINGS_UPDATE_FORM_PROPS} from "../defaults/default-personal-settings-form-props";
+import {InjectionToken} from "@angular/core";
+import {EditFormPropContributorCallback} from "@abp/ng.theme.shared/extensions";
+import {UpdateProfileDto} from "@abp/ng.account.core/proxy";
+
+export const DEFAULT_ACCOUNT_FORM_PROPS = {
+ [eAccountComponents.PersonalSettings]: DEFAULT_PERSONAL_SETTINGS_UPDATE_FORM_PROPS,
+};
+
+export const ACCOUNT_EDIT_FORM_PROP_CONTRIBUTORS = new InjectionToken(
+ 'ACCOUNT_EDIT_FORM_PROP_CONTRIBUTORS',
+);
+
+type EditFormPropContributors = Partial<{
+ [eAccountComponents.PersonalSettings]: EditFormPropContributorCallback[];
+}>;
diff --git a/npm/ng-packs/packages/account/src/lib/tokens/index.ts b/npm/ng-packs/packages/account/src/lib/tokens/index.ts
index 0a88318130..4381d1bc77 100644
--- a/npm/ng-packs/packages/account/src/lib/tokens/index.ts
+++ b/npm/ng-packs/packages/account/src/lib/tokens/index.ts
@@ -1 +1,2 @@
export * from './config-options.token';
+export * from './extensions.token';
diff --git a/npm/ng-packs/packages/theme-shared/extensions/src/lib/components/extensible-form/extensible-form-prop.component.html b/npm/ng-packs/packages/theme-shared/extensions/src/lib/components/extensible-form/extensible-form-prop.component.html
index 4367bd098a..67b0022be8 100644
--- a/npm/ng-packs/packages/theme-shared/extensions/src/lib/components/extensible-form/extensible-form-prop.component.html
+++ b/npm/ng-packs/packages/theme-shared/extensions/src/lib/components/extensible-form/extensible-form-prop.component.html
@@ -3,6 +3,12 @@
*abpPermission="prop.permission; runChangeDetection: false"
[ngSwitch]="getComponent(prop)"
>
+
+
+
+
+
+
;
+ public injectorForCustomComponent: Injector
+
asterisk = '';
options$: Observable[]> = of([]);
@@ -62,15 +65,16 @@ export class ExtensibleFormPropComponent implements OnChanges, AfterViewInit {
readonly!: boolean;
- disabledFn = (data:PropData) => false;
-
- get disabled() {
- return this.disabledFn(this.data)
- }
+ typeaheadModel: any;
private readonly form: FormGroup;
- typeaheadModel: any;
+ disabledFn = (data: PropData) => false;
+
+ get disabled() {
+ return this.disabledFn(this.data)
+ }
+
setTypeaheadValue(selectedOption: ABP.Option) {
this.typeaheadModel = selectedOption || { key: null, value: null };
@@ -108,12 +112,13 @@ export class ExtensibleFormPropComponent implements OnChanges, AfterViewInit {
public readonly track: TrackByService,
protected configState: ConfigStateService,
groupDirective: FormGroupDirective,
+ private injector: Injector
) {
this.form = groupDirective.form;
}
private getTypeaheadControls() {
- const { name } = this.prop;
+ const {name} = this.prop;
const extraPropName = `${EXTRA_PROPERTIES_KEY}.${name}`;
const keyControl =
this.form.get(addTypeaheadTextSuffix(extraPropName)) ||
@@ -133,6 +138,9 @@ export class ExtensibleFormPropComponent implements OnChanges, AfterViewInit {
}
getComponent(prop: FormProp): string {
+ if (prop.template) {
+ return 'template'
+ }
switch (prop.type) {
case ePropType.Boolean:
return 'checkbox';
@@ -173,13 +181,24 @@ export class ExtensibleFormPropComponent implements OnChanges, AfterViewInit {
}
}
- ngOnChanges({ prop }: SimpleChanges) {
- const currentProp = prop?.currentValue;
- const { options, readonly, disabled, validators } = currentProp || {};
+ ngOnChanges({prop}: SimpleChanges) {
+ const currentProp = prop?.currentValue as FormProp;
+ const {options, readonly, disabled, validators, template} = currentProp || {};
+ if (template) {
+ this.injectorForCustomComponent = Injector.create({
+ providers: [
+ {
+ provide: FORM_PROP_DATA_STREAM,
+ useValue: currentProp
+ }
+ ],
+ parent: this.injector,
+ });
+ }
if (options) this.options$ = options(this.data);
if (readonly) this.readonly = readonly(this.data);
-
+
if (disabled) {
this.disabledFn = disabled;
}
diff --git a/npm/ng-packs/packages/theme-shared/extensions/src/lib/components/extensible-table/extensible-table.component.ts b/npm/ng-packs/packages/theme-shared/extensions/src/lib/components/extensible-table/extensible-table.component.ts
index 9682188638..d3c58f0c93 100644
--- a/npm/ng-packs/packages/theme-shared/extensions/src/lib/components/extensible-table/extensible-table.component.ts
+++ b/npm/ng-packs/packages/theme-shared/extensions/src/lib/components/extensible-table/extensible-table.component.ts
@@ -158,15 +158,16 @@ export class ExtensibleTableComponent implements OnChanges {
value,
};
if (prop.value.component) {
- const injector = Injector.create(
- [
+
+ const injector = Injector.create({
+ providers: [
{
provide: PROP_DATA_STREAM,
useValue: value,
},
],
- this.injector,
- );
+ parent: this.injector,
+ });
record[propKey].injector = injector;
record[propKey].component = prop.value.component;
}
diff --git a/npm/ng-packs/packages/theme-shared/extensions/src/lib/models/form-props.ts b/npm/ng-packs/packages/theme-shared/extensions/src/lib/models/form-props.ts
index 44b309d0c2..b607e4cb6e 100644
--- a/npm/ng-packs/packages/theme-shared/extensions/src/lib/models/form-props.ts
+++ b/npm/ng-packs/packages/theme-shared/extensions/src/lib/models/form-props.ts
@@ -38,6 +38,7 @@ export class FormProp extends Prop {
readonly defaultValue: boolean | number | string | Date;
readonly options: PropCallback[]>> | undefined;
readonly id: string | undefined;
+ readonly template? : Type
constructor(options: FormPropOptions) {
super(
@@ -47,6 +48,7 @@ export class FormProp extends Prop {
options.permission,
options.visible,
options.isExtra,
+ options.template
);
this.asyncValidators = options.asyncValidators || (_ => []);
diff --git a/npm/ng-packs/packages/theme-shared/extensions/src/lib/models/props.ts b/npm/ng-packs/packages/theme-shared/extensions/src/lib/models/props.ts
index 66fc4f2c67..0b26512dbc 100644
--- a/npm/ng-packs/packages/theme-shared/extensions/src/lib/models/props.ts
+++ b/npm/ng-packs/packages/theme-shared/extensions/src/lib/models/props.ts
@@ -33,6 +33,7 @@ export abstract class Prop {
public readonly permission: string,
public readonly visible: PropPredicate = _ => true,
public readonly isExtra = false,
+ public readonly template? : Type
) {
this.displayName = this.displayName || this.name;
}
diff --git a/npm/ng-packs/packages/theme-shared/extensions/src/lib/tokens/extensions.token.ts b/npm/ng-packs/packages/theme-shared/extensions/src/lib/tokens/extensions.token.ts
index d2cbbe87bd..28f26317d8 100644
--- a/npm/ng-packs/packages/theme-shared/extensions/src/lib/tokens/extensions.token.ts
+++ b/npm/ng-packs/packages/theme-shared/extensions/src/lib/tokens/extensions.token.ts
@@ -3,6 +3,7 @@ import { ActionCallback, ReadonlyActionData as ActionData } from '../models/acti
import { ExtensionsService } from '../services/extensions.service';
import { Observable } from 'rxjs';
import { ePropType } from '../enums/props.enum';
+ import {FormProp} from "../models/form-props";
export const EXTENSIONS_IDENTIFIER = new InjectionToken('EXTENSIONS_IDENTIFIER');
export type ActionKeys = Extract<'entityActions' | 'toolbarActions', keyof ExtensionsService>;
@@ -24,3 +25,5 @@ export const ENTITY_PROP_TYPE_CLASSES = new InjectionToken(
factory: () => ({} as EntityPropTypeClass),
},
);
+
+export const FORM_PROP_DATA_STREAM = new InjectionToken('FORM_PROP_DATA_STREAM');