客户特定的环境变量或配置(NX & Angular)
Customer specific environment variables or configuration (NX & Angular)
假设我们有两个应用程序,20 位客户正在使用其中一个应用程序。这两个应用程序都使用相同的 table 组件,有 5 列。现在有 5 个客户想要不同的列顺序和列名称。因此,我的第一个想法是为 table 创建一个“默认”配置,如果存在“客户配置”,该配置将被覆盖。
问题:
serve/build 具有特定“客户配置”的应用程序是否已经存在预定义方法?目前我们只有一个应用程序的单一配置,需要为每个客户手动替换。如果您想快速重建所有客户应用程序,这会花费很多时间。
为什么不将服务器中的客户特定配置作为 JSON 文件提供?
我会添加客户特定的 UI 配置,并可能在客户登录后立即获取它。
您最好的前进方式是在运行时加载配置。在 angular 中,profiles/environments 仅为构建时 - 这意味着您在编译期间使用的任何配置文件都将在运行时修复 - 而这不是您想要的。
如何加载 initialization/config 数据的一种常见方法是使用在模块初始化序列期间运行的 APP_INITIALIZER
(doc)。例如,您可能已经在允许访问 UI 之前处理身份验证。
首先,您可以在静态 json 文件中定义每个客户的自定义数据,例如
/assets/
⊢customer1.json
⊢customer2.json
⊢...
⨽customerN.json
但是每次您需要修改配置或添加新客户时,都需要代码 change/build/deployment。一种更灵活的方法是在 API 中提供一个简单的配置端点,例如GET /config
将从数据库中加载给定 user/customer 的自定义配置(基于您随请求发送的身份验证)。这样您就可以自由添加和修改自定义设置,而无需重新部署您的前端应用程序 - 甚至让客户自己调整设置并保存。
让我们看一些代码:
你没有提到,你如何确定当前用户属于哪个客户,或者你如何处理授权,所以你需要填补空白:
/*
Let say we have a simple config object, that contains our customizations
*/
export class CustomerConfig {
columnOrder: string;
someOtherCustomization: string;
}
@Injectable()
export class ConfigurationService {
//this is the config you can then use in your components
config: CustomerConfig
constructor(private http: HttpClient) {}
public init() : Observable<CustomerConfig > {
//somehow validate and obtain the current User
//for example injecting your Auth service or whatever you use
//for demo purposes just a constant
const customerId = 'foobar'
return http.get(`/assets/${customerId}.json`)
.pipe(
tap(configFromFile => this.config = configFromFile )
)
//or, if you have a `/config` API endpoint then just
//return http.get(`/api/config`)....
//of course, you'll need to add your error handling, defaults etc...
}
}
现在是初始化程序(在您的根模块中):
/*
Our initializer factory returning initializer function.
We can inject dependencies/services that we declared bellow in the provider def
Angular will block initialization of this module until the return observable(or promise) completes
*/
function initializeAppFactory(configService: ConfigurationService): () => Observable<any> {
return () => configService.init();
}
@NgModule({
imports: [BrowserModule],
declarations: [AppComponent],
bootstrap: [AppComponent],
providers: [{
provide: APP_INITIALIZER,
useFactory: initializeAppFactory,
multi: true, //there can be more than 1 initializer fn
deps: [ConfigurationService] //dependencies available to our initializer fn
}]
})
export class AppModule {}
现在您可以在需要读取自定义数据的任何组件中注入 ConfigurationService
。
假设我们有两个应用程序,20 位客户正在使用其中一个应用程序。这两个应用程序都使用相同的 table 组件,有 5 列。现在有 5 个客户想要不同的列顺序和列名称。因此,我的第一个想法是为 table 创建一个“默认”配置,如果存在“客户配置”,该配置将被覆盖。
问题: serve/build 具有特定“客户配置”的应用程序是否已经存在预定义方法?目前我们只有一个应用程序的单一配置,需要为每个客户手动替换。如果您想快速重建所有客户应用程序,这会花费很多时间。
为什么不将服务器中的客户特定配置作为 JSON 文件提供?
我会添加客户特定的 UI 配置,并可能在客户登录后立即获取它。
您最好的前进方式是在运行时加载配置。在 angular 中,profiles/environments 仅为构建时 - 这意味着您在编译期间使用的任何配置文件都将在运行时修复 - 而这不是您想要的。
如何加载 initialization/config 数据的一种常见方法是使用在模块初始化序列期间运行的 APP_INITIALIZER
(doc)。例如,您可能已经在允许访问 UI 之前处理身份验证。
首先,您可以在静态 json 文件中定义每个客户的自定义数据,例如
/assets/
⊢customer1.json
⊢customer2.json
⊢...
⨽customerN.json
但是每次您需要修改配置或添加新客户时,都需要代码 change/build/deployment。一种更灵活的方法是在 API 中提供一个简单的配置端点,例如GET /config
将从数据库中加载给定 user/customer 的自定义配置(基于您随请求发送的身份验证)。这样您就可以自由添加和修改自定义设置,而无需重新部署您的前端应用程序 - 甚至让客户自己调整设置并保存。
让我们看一些代码:
你没有提到,你如何确定当前用户属于哪个客户,或者你如何处理授权,所以你需要填补空白:
/*
Let say we have a simple config object, that contains our customizations
*/
export class CustomerConfig {
columnOrder: string;
someOtherCustomization: string;
}
@Injectable()
export class ConfigurationService {
//this is the config you can then use in your components
config: CustomerConfig
constructor(private http: HttpClient) {}
public init() : Observable<CustomerConfig > {
//somehow validate and obtain the current User
//for example injecting your Auth service or whatever you use
//for demo purposes just a constant
const customerId = 'foobar'
return http.get(`/assets/${customerId}.json`)
.pipe(
tap(configFromFile => this.config = configFromFile )
)
//or, if you have a `/config` API endpoint then just
//return http.get(`/api/config`)....
//of course, you'll need to add your error handling, defaults etc...
}
}
现在是初始化程序(在您的根模块中):
/*
Our initializer factory returning initializer function.
We can inject dependencies/services that we declared bellow in the provider def
Angular will block initialization of this module until the return observable(or promise) completes
*/
function initializeAppFactory(configService: ConfigurationService): () => Observable<any> {
return () => configService.init();
}
@NgModule({
imports: [BrowserModule],
declarations: [AppComponent],
bootstrap: [AppComponent],
providers: [{
provide: APP_INITIALIZER,
useFactory: initializeAppFactory,
multi: true, //there can be more than 1 initializer fn
deps: [ConfigurationService] //dependencies available to our initializer fn
}]
})
export class AppModule {}
现在您可以在需要读取自定义数据的任何组件中注入 ConfigurationService
。