我有HeroMockService
将返回模拟数据,HeroService
将回调终端服务以从数据库中检索英雄。
假设Angular2有构建环境,我打算在当前构建环境为HeroMockService
的情况下向AppComponent
注入"dev-mock"
。如果当前构建环境为"dev-rest"
,则应将HeroService
注入AppComponent
。
我想知道如何实现这一目标?
答案 0 :(得分:23)
IMO,更好的选择是使用angular-in-memory-web-api。它模仿Http
使用的后端,因此它不会进行实际的XHR调用,而只是抓取您提供给它的数据。要获得它,只需安装
npm install --save angular-in-memory-web-api
要创建数据库,请在createDb
InMemoryDbService
方法
import { InMemoryDbService } from 'angular-in-memory-web-api'
export class MockData implements InMemoryDbService {
let cats = [
{ id: 1, name: 'Fluffy' },
{ id: 2, name: 'Snowball' },
{ id: 3, name: 'Heithcliff' },
];
let dogs = [
{ id: 1, name: 'Clifford' },
{ id: 2, name: 'Beethoven' },
{ id: 3, name: 'Scooby' },
];
return { cats, dogs, birds };
}
然后配置它
import { InMemoryWebApiModule } from 'angular-in-memory-web-api';
@NgModule({
imports: [
HttpModule,
InMemoryWebApiModule.forRoot(MockData, {
passThruUnknownUrl: true
}),
]
})
现在,当您使用Http
并向/api/cats
发出请求时,它将从数据库中获取所有猫。如果你去/api/cats/1
它会得到第一只猫。您可以执行所有CRUD操作,GET,POST,PUT,DELETE。
需要注意的是,它需要一个基本路径。在示例中,/api
是基本路径。您还可以在配置
InMemoryWebApiModule.forRoot(MockData, {
rootPath: 'root',
passThruUnknownUrl: true // forwards request not in the db
})
现在您可以使用/root/api/cats
。
关于如何从开关切换到生产的问题,您可以使用工厂来创建提供者。如果您使用模拟服务而不是in-memory-web-api
,情况也是如此providers: [
Any,
Dependencies
{
// Just inject `HeroService` everywhere, and depending
// on the environment, the correct on will be chosen
provide: HeroService,
useFactory: (any: Any, dependencies: Dependencies) => {
if (environment.production) {
return new HeroService(any, dependencies);
} else {
return new MockHeroService(any, dependencies);
}
},
deps: [ Any, Dependencies ]
]
就内存网络api而言,我需要回复你(我需要测试一个理论)。我刚刚开始使用它,并且还没有达到我需要切换到生产的程度。现在我只有上面的配置。但我确信有一种方法可以让它无需改变任何东西
好的,对于im-memory-web-api,我们可以做的不是导入模块,而是提供模块提供的XHRBackend
。 XHRBackend
是Http
用于进行XHR调用的服务。内存中的wep-api嘲笑这项服务。所有模块都是这样做的。所以我们可以使用工厂自己提供服务
@NgModule({
imports: [ HttpModule ],
providers: [
{
provide: XHRBackend,
useFactory: (injector: Injector, browser: BrowserXhr,
xsrf: XSRFStrategy, options: ResponseOptions): any => {
if (environment.production) {
return new XHRBackend(browser, options, xsrf);
} else {
return new InMemoryBackendService(injector, new MockData(), {
// This is the configuration options
});
}
},
deps: [ Injector, BrowserXhr, XSRFStrategy, ResponseOptions ]
}
]
})
export class AppHttpModule {
}
请注意BrowserXhr
,XSRFStrategy
和ResponseOptions
依赖项。这就是原始XHRBackend
的创建方式。现在,只需导入HttpModule
。
AppHttpModule
导入您的应用模块
就environment
而言,这是你需要弄清楚的事情。使用angular-cli,当我们构建生产模式时,它已经是一个自动切换到生产环境。
这是我过去用
测试的完整示例import { NgModule, Injector } from '@angular/core';
import { HttpModule, XHRBackend, BrowserXhr,
ResponseOptions, XSRFStrategy } from '@angular/http';
import { InMemoryBackendService, InMemoryDbService } from 'angular-in-memory-web-api';
let environment = {
production: true
};
export class MockData implements InMemoryDbService {
createDb() {
let cats = [
{ id: 1, name: 'Fluffy' }
];
return { cats };
}
}
@NgModule({
imports: [ HttpModule ],
providers: [
{
provide: XHRBackend,
useFactory: (injector: Injector, browser: BrowserXhr,
xsrf: XSRFStrategy, options: ResponseOptions): any => {
if (environment.production) {
return new XHRBackend(browser, options, xsrf);
} else {
return new InMemoryBackendService(injector, new MockData(), {});
}
},
deps: [ Injector, BrowserXhr, XSRFStrategy, ResponseOptions ]
}
]
})
export class AppHttpModule {
}
答案 1 :(得分:21)
见下文我的解决方案基于@peeskillet one。
为您的示例IHeroService
创建一个 mock 和 real 服务实现的接口。
export interface IHeroService {
getHeroes();
}
export class HeroService implements IHeroService {
getHeroes() {
//Implementation goes here
}
}
export class HeroMockService implements IHeroService {
getHeroes() {
//Mock implementation goes here
}
假设您已使用Angular-CLI创建了Angular应用程序,请在environment.ts
文件中添加适当的实现,例如:
import { HeroService } from '../app/hero.service';
export const environment = {
production: false,
heroService: HeroService
};
对于每个不同的environment.(prod|whatever).ts
,您必须定义指向实现的heroService
并添加导入。
现在,假设您要在AppModule类中导入服务(您也可以在需要该服务的组件上执行此操作)
@NgModule({
declarations: [
AppComponent,
],
imports: [
FormsModule,
HttpModule,
AlertModule.forRoot()
],
providers: [
{
provide: 'IHeroService',
useClass: environment.heroService
}
],
bootstrap: [AppComponent]
})
export class AppModule { }
重要的部分是提供者:
providers: [
{
provide: 'IHeroService',
useClass: environment.heroService
}
现在,无论您想在何处使用该服务,都必须执行以下操作:
import { IHeroService } from './../hero.service';
export class HeroComponent {
constructor(@Inject('IHeroService') private heroService: IHeroService) {}
来源: Is it possible to inject interface with angular2? https://medium.com/beautiful-angular/angular-2-and-environment-variables-59c57ba643be http://tattoocoder.com/angular-cli-using-the-environment-option/
答案 2 :(得分:5)
如果将代码组织到Angular2模块中,则可以创建一个附加模块来导入模拟服务,例如:
@NgModule({
providers: [ HeroService ]
})
export class CoreModule {}
假设您为核心模块中的普通服务声明导入:
MockModule
只要您在CoreModule
之后导入HeroService
,就会为HeroMockService
令牌注入的值为MockModule
。这是因为如果同一令牌有两个提供者,Angular将使用最新值。
然后,您可以根据特定值(代表构建环境)自定义// Normal imported modules
var importedModules: Array<any> = [
CoreModule,
BrowserModule,
AppRoutingModule
];
if (process.env.ENV === 'mock') {
console.log('Enabling mocked services.');
importedModules.push(MockModule);
}
@NgModule({
imports: importedModules
})
export class AppModule {}
的导入,例如:
{{1}}
答案 3 :(得分:5)
许多这样的答案是正确的,但是将使摇树失败。模拟服务将作为最终应用程序的一部分包含在内,而这通常是不需要的。此外,切换模拟模式并不容易。
我创建了一个解决以下问题的工作示例:https://github.com/westonpace/angular-example-mock-services
useClass
/ provide
字段来提供接口/抽象类)< / li>
environment.*.ts
文件中加载RealModule或MockModule angular.json
使用的angular-cli
文件进行更改,以创建一个新的构建目标mock
,该目标将通过注入MockModule
的模拟环境文件进行构建。创建一个新的serve
配置,该配置可提供模拟构建,因此您可以进行ng serve -c mock
。更改默认量角器配置,以使其使用模拟的服务目标,以便ng e2e
将针对您的模拟服务运行。答案 4 :(得分:0)
对于像我这样的人会收到以下错误:
ERROR in Error encountered resolving symbol values statically.
Function calls are not supported. Consider replacing the function
or lambda with a reference to an exported function
出于我的目的,它适用于ErrorHandler:
{
provide: ErrorHandler,
useFactory: getErrorHandler,
deps: [ Injector ]
}
...
export function getErrorHandler(injector: Injector): ErrorHandler {
if (environment.production) {
return new MyErrorHandler(injector);
} else {
return new ErrorHandler();
}
}
答案 5 :(得分:0)
Luuk G在这篇文章中提供了一个非常简单的解决方案。
https://hackernoon.com/conditional-module-imports-in-angular-518294aa4cc
总结:
let dev = [
StoreDevtoolsModule.instrument({
maxAge: 10,
}),
];
// if production clear dev imports and set to prod mode
if (process.env.NODE_ENV === 'production') {
dev = [];
enableProdMode();
}
@NgModule({
bootstrap: [
Base,
],
declarations: [
Base,
],
imports: [
Home,
RouterModule.forRoot(routes, { useHash: true }),
StoreModule.forRoot(reducers.reducerToken),
...dev,
],
providers: [
],
})
export class Root { }
答案 6 :(得分:0)
这在Angular 6中对我有用。利用@void's Interface approach(其中真正的服务和模拟服务都实现服务接口)。接受的答案忽略了提及工厂的目的是返回接口的实现。
import { NgModule, ModuleWithProviders } from '@angular/core';
import { CommonModule } from '@angular/common';
import { IHeroService } from './ihero.service';
import { HeroService } from './hero.service';
import { MockHeroService } from './mock-hero.service';
@NgModule({
imports: [
CommonModule
],
})
export class HeroServiceModule {
static forRoot(mock: boolean): ModuleWithProviders {
return {
ngModule: HeroServiceModule ,
providers: [
{
provide: IHeroService,
useClass: mock ? MockHeroService : HeroService,
},
],
};
}
}
import { NgModule, isDevMode } from '@angular/core';
import { HeroServiceModule } from './heroservice/heroservice.module';
// ...
imports: [
BrowserModule,
HttpClientModule,
// ...
HeroServiceModule.forRoot(
isDevMode() // use mock service in dev mode
),
],
// ...
导入和使用界面的方式与实际服务相同(实现将在运行时“提供”。)
import { IHeroService } from '../heroservice/iheroservice.service';
// ...
答案 7 :(得分:0)
这是使用条件环境逻辑执行此操作的最新方法的示例。请注意,EventService和LogService只是您可能拥有的其他服务的示例。
providers: [
EventService,
LogService,
MyService,
{
provide: MyService,
useClass: (environment.custom == null || environment.custom === '' || environment.custom === 'MyCustomerService')
? MyCustomService : MyService
}
]
]
答案 8 :(得分:-1)
@Inject(&#39; IHeroService&#39;)在生产方面做得不好。就我而言,我只是这样做了:
import { Injectable } from '@angular/core';
import * as Priority from 'priority-web-sdk';
@Injectable()
export class PriorityService
{
priority;
constructor( ){
this.priority=Priority;
}
}
我要导入的Priority
库没有导出的模块,所以我需要作弊。
起初我尝试@Inject('IHeroService')
方式但是当我编译为prod时它没有工作。
希望它有所帮助!