-
ABP Framework version: v4.3.1
-
UI type: Angular
-
DB provider: EF Core
-
Tiered (MVC) or Identity Server Separated (Angular): no / no
-
Exception message and stack trace:
-
Steps to reproduce the issue:
Hello. I am trying to add boolean field to Personal Setting (Personal info) in Angular UI. Field already exists in AbpUsers table.
I am following this guide https://docs.abp.io/en/abp/latest/UI/Angular/Component-Replacement.
First I installed module @abp/ng.account with yarn so I can get eAccountComponents.PersonalSettings for replacing. After I installed module I got bunch of errors in others components (only html classes)...
After that I created my custom component and added replaceble logic to AppComponent
import { Component } from '@angular/core';
import { CustomPersonalSettingsComponentComponent } from './custom-personal-settings-component/custom-personal-settings-component.component';
import { ReplaceableComponentsService } from '@abp/ng.core';
import { eAccountComponents } from '@abp/ng.account';
@Component({
selector: 'app-root',
template: `
<abp-loader-bar></abp-loader-bar>
<abp-dynamic-layout></abp-dynamic-layout>
`,
})
export class AppComponent {
constructor(
private replaceableComponents: ReplaceableComponentsService, // injected the service
) {
this.replaceableComponents.add({
component: CustomPersonalSettingsComponentComponent,
key: eAccountComponents.PersonalSettings,
});
}
}
After adding imports, I got error that abp-dynamics-layout is not a known element...
Even with all that errors, my project compiled successfully and I can use all my broken components (html). When I go to Personal info, it still show default component, not mine...
Anyone know why I got these errors in html after installing @abp/ng.account and why PersonalInfo replacing is not working? If I try and replace Manage profile component it works...
13 Answer(s)
-
0
I fixed problems with html components.
First I cleared cache, then deleted node_modules folder and reinstalled all modules with yarn install... Now I dont have any errors with html, but replacing PageSettings component still not working...
-
0
Any answer to this?
-
0
Hi,
Please follow the steps below to replace personal settings component:
-
Run the following command to generate account module:
yarn ng generate module account
-
Run command below to generate a component called
CustomPersonalSettings
:
yarn ng generate component account/custom-personal-settings --skip-tests --inline-style
-
Replace the generated
src/account/account.module.ts
content with following:
import { NgModule } from '@angular/core'; import { AccountPublicModule } from '@volo/abp.ng.account/public'; import { SharedModule } from '../shared/shared.module'; import { CustomPersonalSettingsComponent } from './custom-personal-settings/custom-personal-settings.component'; @NgModule({ declarations: [CustomPersonalSettingsComponent], imports: [AccountPublicModule.forChild({}), SharedModule], }) export class AccountModule {}
-
Replace the generated
src/account/custom-personal-settings/custom-personal-settings.component.ts
content with following:
import { ConfigStateService, ProfileService, SubscriptionService } from '@abp/ng.core'; import { Confirmation, ConfirmationService, ToasterService } from '@abp/ng.theme.shared'; import { Component, EmbeddedViewRef, OnInit } from '@angular/core'; import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { AccountService, ManageProfileStateService, ProfileResponse, } from '@volo/abp.ng.account/public'; import { Observable } from 'rxjs'; import { filter, tap } from 'rxjs/operators'; @Component({ selector: 'app-custom-personal-settings', templateUrl: './custom-personal-settings.component.html', styles: [], }) export class CustomPersonalSettingsComponent implements OnInit { storedProfile: ProfileResponse; profile$: Observable = this.manageProfileState.getProfile$(); modalVisible = false; modalBusy: boolean = false; modalRef: EmbeddedViewRef; form: FormGroup; token: string = ''; isEmailUpdateEnabled: boolean = true; isUserNameUpdateEnabled: boolean = true; showEmailVerificationBtn$ = this.manageProfileState.createStateStream( state => !state.hideEmailVerificationBtn ); get userId(): string { return this.configState.getDeep('currentUser.id'); } askForImmediatePhoneNumberVerification = () => { this.confirmationService .info('AbpAccount::DoYouWantToVerifyPhoneNumberMessage', 'AbpAccount::ConfirmYourPhoneNumber') .pipe(filter(status => status === Confirmation.Status.confirm)) .subscribe(this.initPhoneNumberConfirmation); }; checkPhoneNumberChanges = ({ phoneNumber }: ProfileResponse) => { if (phoneNumber && phoneNumber !== this.storedProfile?.phoneNumber) { this.askForImmediatePhoneNumberVerification(); } }; buildForm = (profile: ProfileResponse) => { this.form = this.fb.group({ userName: [profile.userName, [Validators.required, Validators.maxLength(256)]], email: [profile.email, [Validators.required, Validators.email, Validators.maxLength(256)]], name: [profile.name || '', [Validators.maxLength(64)]], surname: [profile.surname || '', [Validators.maxLength(64)]], phoneNumber: [profile.phoneNumber || '', [Validators.maxLength(16)]], }); }; initPhoneNumberConfirmation = () => { this.accountService .sendPhoneNumberConfirmationToken({ phoneNumber: this.form.value.phoneNumber, userId: this.userId, }) .pipe(tap(() => (this.token = ''))) .subscribe(this.openModal); }; openModal = () => { this.modalVisible = true; }; removeModal = () => { this.modalVisible = false; }; setPhoneNumberAsConfirmed = () => { this.storedProfile.phoneNumberConfirmed = true; }; constructor( private fb: FormBuilder, private accountService: AccountService, private toasterService: ToasterService, private confirmationService: ConfirmationService, private configState: ConfigStateService, private subscription: SubscriptionService, private manageProfileState: ManageProfileStateService, private profileService: ProfileService ) {} confirmPhoneNumber() { this.accountService .confirmPhoneNumber({ token: this.token, userId: this.userId }) .pipe(tap(this.setPhoneNumberAsConfirmed), tap(this.removeModal)) .subscribe(() => { this.toasterService.success('AbpAccount::Verified', '', { life: 5000 }); }); } sendEmailVerificationToken() { this.accountService .sendEmailConfirmationToken({ appName: 'Angular', email: this.form.value.email, userId: this.userId, }) .subscribe(() => { this.toasterService.success('AbpAccount::EmailConfirmationSentMessage', '', { messageLocalizationParams: [this.form.value.email], }); this.manageProfileState.setHideEmailVerificationBtn(true); }); } ngOnDestroy() { this.removeModal(); } ngOnInit() { this.subscription.addOne( this.profile$.pipe( filter(Boolean), tap(profile => (this.storedProfile = profile)), tap(this.checkPhoneNumberChanges) ), this.buildForm ); const settings = this.configState.getSettings(); this.isEmailUpdateEnabled = (settings['Abp.Identity.User.IsEmailUpdateEnabled'] || '').toLowerCase() !== 'false'; this.isUserNameUpdateEnabled = (settings['Abp.Identity.User.IsUserNameUpdateEnabled'] || '').toLowerCase() !== 'false'; } submit() { if (this.form.invalid) return; const { phoneNumberConfirmed, ...profile } = this.form.value; this.profileService.update(profile).subscribe(res => { this.manageProfileState.setProfile(res); this.toasterService.success('AbpAccount::PersonalSettingsSaved', '', { life: 5000 }); }); } }
-
Replace the generated
src/account/custom-personal-settings/custom-personal-settings.component.html
content with following:
**{{ 'AbpAccount::Verify' | abpLocalization }}
{{ 'AbpAccount::ConfirmationTokenSentMessage' | abpLocalization: form.value.phoneNumber }}
-
Open
src/app-routing.module.ts
file modifyaccount
route like this:
{ path: 'account', loadChildren: () => import('./account/account.module').then(m => m.AccountModule), },
-
Open
src/app.component.ts
and replace thePersonalSettingsComponent
as shown below:
import { Component, OnInit } from '@angular/core'; import { eAccountManageProfileTabNames, ManageProfileTabsService, } from '@volo/abp.ng.account/public/config'; import { CustomPersonalSettingsComponent } from './account/custom-personal-settings/custom-personal-settings.component'; @Component({ selector: 'app-root', template: ` <abp-loader-bar></abp-loader-bar> <abp-dynamic-layout></abp-dynamic-layout> `, }) export class AppComponent implements OnInit { constructor( private manageProfileTabs: ManageProfileTabsService ) {} ngOnInit() { this.manageProfileTabs.patch(eAccountManageProfileTabNames.PersonalInfo, { component: CustomPersonalSettingsComponent, }); } }
You can replace a tab in the my profile page by using the
ManageProfileTabsService
.I hope this helps you.
-
-
0
Hello.
Thanks, it works. Is there any guide on how to add aditional field to Profile.Response or how to extend Profile class and module to add new field so user can update it?
-
0
I am following this guide here: https://docs.abp.io/en/abp/latest/Customizing-Application-Modules-Overriding-Services#extending-data-transfer-objects but I don't have class YourProjectNameDtoExtensions in my solution.
I created application with abp suite version 4.3.0.
-
0
hi
You can create a
MyProjectNameDtoExtensions
file in yourApplication.Contracts
layer.I will add this file to next version of app pro template.
-
0
Hi, thanks for answer.
I created file but I can not extend ProfileDto and UpdateProfileDto. What am I missing? I have boolean field EmailNotifications, which I added to AppUser class and to MyProjectExtensionConfigurator. It shows on Administrations/Identity Managament/Users and also it works when I edit it there. Now I want to add this field to Personal info, so user can check/uncheck it himself. I followed your guide but can not add my custom property to ProfileDto. I found this line of code which populated ExtraProperties of ProfileDto but not also UpdateProfileDto. What am I doing wrong?
Thanks for your answers!
-
0
hi beuko
Can you share your full code about extending-data-transfer-objects?
-
0
AppUser.cs
//added this line public bool EmailNotifications { get; protected set; }
Application.Contracts/SKAPDtoExtensions.cs
public static class SKAPDtoExtensions { private static readonly OneTimeRunner OneTimeRunner = new OneTimeRunner(); public static void Configure() { OneTimeRunner.Run(() => { /* You can add extension properties to DTOs * defined in the depended modules. * * Example: * * ObjectExtensionManager.Instance * .AddOrUpdateProperty("Title"); * * See the documentation for more: * https://docs.abp.io/en/abp/latest/Object-Extensions */ ObjectExtensionManager.Instance .AddOrUpdateProperty( new[] { typeof(ProfileDto), typeof(UpdateProfileDto) }, "EmailNotifications" ); }); } }
Domain.Shared/SKAPModuleExtensionConfigurator.cs
public static class SKAPModuleExtensionConfigurator { private static readonly OneTimeRunner OneTimeRunner = new OneTimeRunner(); public static void Configure() { OneTimeRunner.Run(() => { ConfigureExistingProperties(); ConfigureExtraProperties(); }); } private static void ConfigureExtraProperties() { /* You can configure extra properties for the * entities defined in the modules used by your application. * * This class can be used to define these extra properties * with a high level, easy to use API. * * Example: Add a new property to the user entity of the identity module ObjectExtensionManager.Instance.Modules() .ConfigureIdentity(identity => { identity.ConfigureUser(user => { user.AddOrUpdateProperty( //property type: string "SocialSecurityNumber", //property name property => { //validation rules property.Attributes.Add(new RequiredAttribute()); property.Attributes.Add(new StringLengthAttribute(64) {MinimumLength = 4}); //...other configurations for this property } ); }); }); * See the documentation for more: * https://docs.abp.io/en/abp/latest/Module-Entity-Extensions */ ObjectExtensionManager.Instance.Modules() .ConfigureIdentity(identity => { identity.ConfigureUser(user => { user.AddOrUpdateProperty( "EmailNotifications", options => { options.DefaultValue = false; } ); }); }); } }
-
0
Hi,
The profile UI does not support object extension, you can customize it via https://docs.abp.io/en/abp/latest/UI/Angular/Component-Replacement
-
0
Hello liangshiwei,
I already customized angular UI to show this field, now I need to get value from database (via DTO extensions) and also save it back to database.
-
0
Hi,
I think there is no problem, you can get value via API. and update profile.
Don't forget call
SKAPDtoExtensions.Configure()
method in theApplicationContractsModule
-
0
This question has been automatically marked as stale because it has not had recent activity.