diff --git a/docs/en/framework/ui/angular/environment.md b/docs/en/framework/ui/angular/environment.md
index 4395141c13..887bd3d4ec 100644
--- a/docs/en/framework/ui/angular/environment.md
+++ b/docs/en/framework/ui/angular/environment.md
@@ -108,6 +108,80 @@ export interface RemoteEnv {
- `method`: HTTP method to be used when retrieving environment config. Default: `GET`
- `headers`: If extra headers are needed for the request, it can be set through this field.
+### Example RemoteEnv Configuration
+
+To enable dynamic environment configuration at runtime, add the `remoteEnv` property to your environment file:
+
+```ts
+export const environment = {
+ // ... other configurations
+ remoteEnv: {
+ url: '/getEnvConfig',
+ mergeStrategy: 'deepmerge'
+ }
+} as Environment;
+```
+
+### Web Server Configuration
+
+When using `remoteEnv` with a URL like `/getEnvConfig`, you need to configure your web server to serve the `dynamic-env.json` file with the correct content type (`application/json`). Otherwise, the application may receive HTML (e.g., your `index.html`) instead of JSON, causing configuration to fail silently.
+
+#### IIS Configuration (web.config)
+
+```xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+```
+
+#### Nginx Configuration
+
+```nginx
+server {
+ listen 80;
+ listen [::]:80;
+ server_name _;
+
+ location / {
+ root /usr/share/nginx/html;
+ index index.html index.htm;
+ try_files $uri $uri/ /index.html =404;
+ }
+
+ location /getEnvConfig {
+ default_type 'application/json';
+ add_header 'Access-Control-Allow-Origin' '*' always;
+ add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always;
+ add_header 'Content-Type' 'application/json';
+ root /usr/share/nginx/html;
+ try_files $uri /dynamic-env.json;
+ }
+
+ error_page 500 502 503 504 /50x.html;
+ location = /50x.html {
+ root /usr/share/nginx/html;
+ }
+}
+```
+
## Configure Core Provider with Environment
`environment` variable comes from angular host application.
diff --git a/npm/ng-packs/packages/core/src/lib/tests/environment-utils.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/environment-utils.spec.ts
index bc1af8e5f4..353f1ebe69 100644
--- a/npm/ng-packs/packages/core/src/lib/tests/environment-utils.spec.ts
+++ b/npm/ng-packs/packages/core/src/lib/tests/environment-utils.spec.ts
@@ -1,7 +1,7 @@
import { HttpClient } from '@angular/common/http';
import { Component, Injector } from '@angular/core';
import { createComponentFactory, Spectator } from '@ngneat/spectator/vitest';
-import { BehaviorSubject } from 'rxjs';
+import { BehaviorSubject, throwError } from 'rxjs';
import { Environment, RemoteEnv } from '../models/environment';
import { EnvironmentService } from '../services/environment.service';
import { getRemoteEnv } from '../utils/environment-utils';
@@ -85,7 +85,6 @@ describe('EnvironmentUtils', () => {
injectorSpy.mockReturnValueOnce(environmentService);
injectorSpy.mockReturnValueOnce(http);
- injectorSpy.mockReturnValueOnce({});
requestSpy.mockReturnValue(new BehaviorSubject(customEnv));
@@ -95,5 +94,24 @@ describe('EnvironmentUtils', () => {
expect(requestSpy).toHaveBeenCalledWith('GET', '/assets/appsettings.json', { headers: {} });
expect(setStateSpy).toHaveBeenCalledWith(expectedValue);
}
+
+ it('should handle request error gracefully and use local environment', async () => {
+ const injector = spectator.inject(Injector);
+ const injectorSpy = jest.spyOn(injector, 'get');
+ const http = spectator.inject(HttpClient);
+ const requestSpy = jest.spyOn(http, 'request');
+ const environmentService = spectator.inject(EnvironmentService);
+ const setStateSpy = jest.spyOn(environmentService, 'setState');
+
+ injectorSpy.mockReturnValueOnce(environmentService);
+ injectorSpy.mockReturnValueOnce(http);
+
+ requestSpy.mockReturnValue(throwError(() => new Error('Network error')));
+
+ environment.remoteEnv.mergeStrategy = 'deepmerge';
+ await getRemoteEnv(injector, environment);
+
+ expect(setStateSpy).toHaveBeenCalledWith(deepMerge(environment, {}));
+ });
});
});
diff --git a/npm/ng-packs/packages/core/src/lib/utils/environment-utils.ts b/npm/ng-packs/packages/core/src/lib/utils/environment-utils.ts
index 8db9b5e02f..ae11ef002a 100644
--- a/npm/ng-packs/packages/core/src/lib/utils/environment-utils.ts
+++ b/npm/ng-packs/packages/core/src/lib/utils/environment-utils.ts
@@ -1,10 +1,9 @@
import { HttpClient } from '@angular/common/http';
-import { Injector } from '@angular/core';
+import { Injector, isDevMode } from '@angular/core';
import { of } from 'rxjs';
import { catchError, tap } from 'rxjs/operators';
import { Environment, RemoteEnv } from '../models/environment';
import { EnvironmentService } from '../services/environment.service';
-import { HttpErrorReporterService } from '../services/http-error-reporter.service';
import { deepMerge } from './object-utils';
export function getRemoteEnv(injector: Injector, environment: Partial) {
@@ -15,15 +14,20 @@ export function getRemoteEnv(injector: Injector, environment: Partial(method, url, { headers })
.pipe(
catchError(err => {
- httpErrorReporter.reportError(err);
+ if (isDevMode()) {
+ console.warn(
+ `[ABP Environment] Failed to fetch remote environment from "${url}". ` +
+ `Error: ${err.message || err}\n` +
+ `See https://abp.io/docs/latest/framework/ui/angular/environment#example-remoteenv-configuration for configuration details.`,
+ );
+ }
return of(null);
- }), // TODO: Consider get handle function from a provider
+ }),
tap(env =>
environmentService.setState(
mergeEnvironments(environment, env || ({} as Environment), remoteEnv as RemoteEnv),