How can I force all validation errors to show in an Angular form?
I've got a simple form with a required field
this.form = this.fb.group({
name: [name ?? null, [Validators.required]]
});
But when I click submit, no errors are shown.
submitForm()
{
if(!this.form.valid)
{
// How do I show the errors?
return;
}
// ...
}
If I type something into the field, then delete it, the required validation message shows.
I want to show all validation errors when the user tries to submit an invalid form.
How do I set the default culture and UI culture of my Angular app?
I've configured my FooApiHostModule like so
private void ConfigureLocalization()
{
Configure<AbpLocalizationOptions>(options =>
{
options.Languages.Clear();
options.Languages.Add(new LanguageInfo("en-GB", "en-GB", "English"));
});
}
I've duplicated the en.json localization file and renamed it en-GB, I've changed the Culture
elemein in the top of that file like so
{
"Culture": "en-GB",
"Texts": {
...
}
}
If I put a breakpoint in a controller action, and inspect System.Threading.Thread.CurrentThread.CurrentCulture
and System.Threading.Thread.CurrentThread.CurrentUICulture
they're both "en", not "en-GB".
How do I get my application to run in en-GB
?
I've also cahnged my Startup.Configure()
method like so, but this doesn't fix it either.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory)
{
app.InitializeApplication();
app.UseRequestLocalization(new RequestLocalizationOptions
{
DefaultRequestCulture = new RequestCulture("en-GB"),
SupportedCultures =
{
new CultureInfo("en-GB")
},
SupportedUICultures =
{
new CultureInfo("en-GB")
}
});
}
I've got an entity with a backing field navigation property like so.
public class Customer : FullAuditedAggregateRoot<Guid>, IHasTimelineTopic, IHasAttachments
{
private readonly HashSet<Contact> _contacts = new();
public IEnumerable<Contact> Contacts => _contacts?.ToList();
public void AddContacts(IEnumerable<Contact> contacts)
{
Check.NotNull(contacts, nameof(contacts));
foreach (var contact in contacts)
{
_contacts.Add(contact);
}
}
public void RemoveContacts(IEnumerable<Contact> contacts)
{
Check.NotNull(contacts, nameof(contacts));
foreach (var contact in contacts)
{
_contacts.Remove(contact);
}
}
}
it's EF config looks like
builder.Entity<Customer>(b =>
{
b.ToTable(FooConsts.DbTablePrefix + "Customers", FooConsts.DbSchema);
b.ConfigureByConvention();
b.HasMany(x => x.Contacts).WithMany(x => x.Customers)
.UsingEntity(join => join.ToTable("AppCustomerContacts"));
b.Navigation(nameof(Customer.Contacts))
.UsePropertyAccessMode(PropertyAccessMode.Field);
});
In my AppService I can update it like so
[Authorize(FooPermissions.Customers.Edit)]
public virtual async Task<CustomerDto> UpdateAsync(Guid id, CustomerUpdateDto input)
{
var customer = await _customerRepository.GetAsync(id);
ObjectMapper.Map(input, customer);
var contacts = await _contactRepository.FindListAsync(input.ContactIds);
customer.AddContacts(contacts);
var toRemove = customer.Contacts.Where(x => input.ContactIds.Contains(x.Id) == false).ToList();
customer.RemoveContacts(toRemove);
customer = await _customerRepository.UpdateAsync(customer, autoSave: true);
return ObjectMapper.Map<Customer, CustomerDto>(customer);
}
this works fine and generates two SQL statements, one to update the AppCustomerContacts
xref table and one to update the entity itself.
I'm trying to write a console aplication to load some data from an Excel file. I followed the strucure of the included DbMigratior app and I've got :
IHostedService
and takes an IHostApplicationLifetime
as a constructor parameter. It has a StartAsync()
method which does just like the DbMigratior. It creates a DataSeederModule
from the AbpApplicationfactory
, calls UseAutoFac()
and AddLogging()
. Initializes the application, gets a DataSeedingService
from the ServiceProvider
and calls my SeedAsync()
method.DataSeedingService
is an IDomainService
which takes a few repositories, the IGuidGenerator
and IUnitOfWork
as constructor paramerters.The DataSeedingService
adds Contacts to my Customers in the same way as above, using .AddContacts()
but when I call await _customerRepository.UpdateAsync(customer, true);
it only generates a single SQL statement to update the entity and doesn't insert anything into AppCustomerContacts
.
What am I missing?
Here's how SeedAsync()
calls the
contact = await _contactRepository.InsertAsync(contact, true);
customer = await _customerRepository.GetAsync(customerId);
customer.AddContact(contact);
_logger.LogInformation($"\t Adding contact to customer {customer.Name}");
customer = await _customerRepository.UpdateAsync(customer, true);
My DataSeederModule
looks like this
[DependsOn(
typeof(AbpAutofacModule),
typeofFooDomainModule),
typeof(EFooEntityFrameworkCoreModule),
typeof(FooApplicationContractsModule)
)]
public class FooDataSeederModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<AbpBackgroundJobOptions>(options =>
{
options.IsJobExecutionEnabled = false;
});
}
}
Why isn't my console app generating two SQL statements? What am I missing? Do I need to reference another module or configure something at app startup?
I can't get login to work reliably after deploying to a server. It works sometimes, at a guess about 3% of the time, but I can't reproduce it reliably to get an accurate number. It only happens once deployed, but I've deployed to two separate environments, a VPS and Azure and ended up with the same situation. This suggests it's something I'm doing (or not doing) but the lack of deployment instructions makes it hard to know.
Here's what I'm doing:
import { Config } from '@abp/ng.core';
/* web URL */
const baseUrl = 'https://example.z33.web.core.windows.net';
export const environment = {
production: true,
application: {
baseUrl,
name: 'MyApp',
},
oAuthConfig: {
issuer: 'https://example-api.azurewebsites.net',
redirectUri: baseUrl,
clientId: 'MyApp_App',
responseType: 'code',
scope: 'offline_access MyApp',
},
apis: {
default: {
url: 'https://example-api.azurewebsites.net',
rootNamespace: 'MyApp',
},
},
} as Config.Environment;
"configurations": {
"production": {
...
},
"azure": {
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.azure.ts"
}
],
"optimization": true,
...
}
}
yarn build:azure
and upload the output to Static Website in blob storage at https://example.z33.web.core.windows.net.{
"App": {
"SelfUrl": "https://example-api.azurewebsites.net",
"ClientUrl": "https://example.z33.web.core.windows.net",
"CorsOrigins": "https://example.z33.web.core.windows.net,http://example.z33.web.core.windows.net"
},
"ConnectionStrings": {
"Default": "xxxx"
},
"AuthServer": {
"Authority": "https://example-api.azurewebsites.net",
"RequireHttpsMetadata": "true"
},
}
"ExcelErp_App": {
"ClientId": "ExcelErp_App",
"RootUrl": "https://example.z33.web.core.windows.net"
},
(as per https://support.abp.io/QA/Questions/524/UNAUTHORIZEDCLIENT-after-deploying-to-test-server, no trailing slash)
9. I run the DbMigrator and confirm the tables have been created and the seed data added. The Admin user is there and if I check [IdentityServerClientRedirectUris]
I can see a row for
ClientId RedirectUri
CDFEC9FC-51C9-A562-089C-39F953F0ECAB https://example.z33.web.core.windows.net
What's happening? What am I doing wrong?
I want to integrate the file management system into my UI. I want my users to be able to select a file to associate it with an entity.
How can I put a file picker component in my Angular component so the user can pick a file from the File Mangement system?
Suite is generating invalid C#. It's looks like it's leaving a replacement token on the end of property definitions.
I'm using Suite 3.3.1
[StringLength(BomListConsts.DescriptionMaxLength)]
public string Description { get; set; } %%initial-value%%
I've tried creating a brand new Suite project and adding a simple Foo entity with a sinle int property and I get the following code for the DTOs.
public class FoooCreateDto
{
public int? F1 { get; set; } %%initial-value%%
}
public class FoooDto : FullAuditedEntityDto<Guid>
{
public int? F1 { get; set; } %%initial-value%%
}
public class FoooUpdateDto
{
public int? F1 { get; set; } %%initial-value%%
}
The tool is absolutely unusable and I've wasted half a day trying different things to get it working thinking it was something wrong with my object definition.
This is unacceptable.
Please advise
Check the docs before asking a question: https://docs.abp.io/en/commercial/latest/ Check the samples, to see the basic tasks: https://docs.abp.io/en/commercial/latest/samples/index The exact solution to your question may have been answered before, please use the search on the homepage.
500 Internal Server Error UNAUTHORIZED_CLIENT
2020-11-02 11:04:48.240 +00:00 [ERR] Invalid redirect_uri: https://dev-web.example.com/ {"ClientId":"Example_App","ClientName":"Example_App","RedirectUri":null,"AllowedRedirectUris":["https://dev-api.example.com","https://dev-web.example.com"],"SubjectId":"anonymous","ResponseType":null,"ResponseMode":null,"GrantType":null,"RequestedScopes":"","State":null,"UiLocales":null,"Nonce":null,"AuthenticationContextReferenceClasses":null,"DisplayMode":null,"PromptMode":null,"MaxAge":null,"LoginHint":null,"SessionId":null,"Raw":{"response_type":"code","client_id":"Example_App","state":"MldHMGt1TnZkS0ItcnBXQzU1WGROck9LTWp5MGlzdFJEdjV0UE9sMS5DUjdD","redirect_uri":"https://dev-web.example.com/","scope":"openid offline_access Example","code_challenge":"vXXK9P8NZ7zMPbzktJetY4amyp5F1sqWU-rYTpQPJ3c","code_challenge_method":"S256","nonce":"MldHMGt1TnZkS0ItcnBXQzU1WGROck9LTWp5MGlzdFJEdjV0UE9sMS5DUjdD"},"$type":"AuthorizeRequestValidationLog"}
import { Config } from '@abp/ng.core';
const baseUrl = 'https://dev-web.example.com/';
export const environment = {
production: true,
application: {
baseUrl,
name: 'Example',
},
oAuthConfig: {
issuer: 'https://dev-api.example.com',
redirectUri: baseUrl,
clientId: 'Example_App',
responseType: 'code',
scope: 'offline_access Example',
},
apis: {
default: {
url: 'https://dev-api.example.com',
rootNamespace: 'Example',
},
},
} as Config.Environment;
"build:dev": "ng build --configuration development"
and yarn build:dev
{
"ConnectionStrings": {
"Default": "Server=localhost\\sqlexpress;Database=Example-Dev;Trusted_Connection=True;MultipleActiveResultSets=true"
},
"IdentityServer": {
"Clients": {
"Example_Web": {
"ClientId": "Example_Web",
"RootUrl": "https://dev-web.example.com/"
},
"Example_App": {
"ClientId": "Example_App",
"RootUrl": "https://dev-api.example.com/"
},
"Example_Blazor": {
"ClientId": "Example_Blazor",
"RootUrl": "https://localhost:44307"
}
}
},
"AbpLicenseCode": "..."
}
{
"App": {
"SelfUrl": "https://dev-api.example.com/",
"ClientUrl": "https://dev-web.example.com/",
"CorsOrigins": "http://dev-web.example.com/,https://dev-web.example.com/"
},
"ConnectionStrings": {
"Default": "Server=localhost\\SQLExpress;Database=Example-Dev;Trusted_Connection=True;MultipleActiveResultSets=true"
},
"AuthServer": {
"Authority": "https://dev-api.example.com",
"RequireHttpsMetadata": "true"
},
}
The Issue
I can browse the Swagger document and the Angular app fine, but when I try to log in I get the error screen from the API stating
500
Internal Server Error
UNAUTHORIZED_CLIENT
and the following messages in the log 2020-11-02 11:04:48.240 +00:00 [ERR] Invalid redirect_uri: https://dev-web.example.com/ {"ClientId":"Example_App","ClientName":"Example_App","RedirectUri":null,"AllowedRedirectUris":["https://dev-api.example.com","https://dev-web.example.com"],"SubjectId":"anonymous","ResponseType":null,"ResponseMode":null,"GrantType":null,"RequestedScopes":"","State":null,"UiLocales":null,"Nonce":null,"AuthenticationContextReferenceClasses":null,"DisplayMode":null,"PromptMode":null,"MaxAge":null,"LoginHint":null,"SessionId":null,"Raw":{"response_type":"code","client_id":"Example_App","state":"MldHMGt1TnZkS0ItcnBXQzU1WGROck9LTWp5MGlzdFJEdjV0UE9sMS5DUjdD","redirect_uri":"https://dev-web.example.com/","scope":"openid offline_access Example","code_challenge":"vXXK9P8NZ7zMPbzktJetY4amyp5F1sqWU-rYTpQPJ3c","code_challenge_method":"S256","nonce":"MldHMGt1TnZkS0ItcnBXQzU1WGROck9LTWp5MGlzdFJEdjV0UE9sMS5DUjdD"},"$type":"AuthorizeRequestValidationLog"}
What's wrong, and what is the processess for deploying an appilcation to a live environment?
Check the docs before asking a question: https://docs.abp.io/en/commercial/latest/ Check the samples, to see the basic tasks: https://docs.abp.io/en/commercial/latest/samples/index The exact solution to your question may have been answered before, please use the search on the homepage.
The documenation makes reference to DDD and describes the use of Entities and Aggregate Roots but I can't see an example of the right* way to implement this.
When you generate an Entity (FullyAuditedEntity) with ABP Suite it generates an AppService, Repository, UI and all the other bits for that entity. My understanding (which is far from complete admittedly).
Let's take an example of a simple order form or invoice (not ecommerce). The user creates a new Invoice
(Aggregate Root) and adds LineItem
s to it. This is pretty much the canoncial example of an Aggreagate Root (, right?) and we're told the Invoice should be bring with it the line items, and then you add new items to that Aggregate Root, and then persist the Aggregate Root and it's line items as a whole.
How does this map to the patterns in ABP? Should we use standard EF functionality to Include()
the line items, map those to LineItemDto
s and put a IList<LineItemDto>
on the Invoice aggregate root? Or should we use the service and repository provided when we create the entity?
Thinking specifically about the Angular UI, should we be loading the line items in a separate AJAX call or do they come from the Invoice service?
As a new ABP customer I feel this information is missing from the documentation and that an explanation of this canonical DDD example would be a great addition to the platform.
Thanks, Greg
Check the docs before asking a question: https://docs.abp.io/en/commercial/latest/ Check the samples, to see the basic tasks: https://docs.abp.io/en/commercial/latest/samples/index The exact solution to your question may have been answered before, please use the search on the homepage.
How can I add a custom theme to an ABP app using Angular UI?