我正试图弄清楚如何在Angular 11中使用useFactory作为异步函数。现在我有这个:
import { ApolloClientOptions } from 'apollo-client';
import { FirebaseService } from './firebase.service';
// other imports here...
export async function createApollo(httpLink: HttpLink, fs: FirebaseService): Promise<ApolloClientOptions<any>> {
const token = await fs.getToken();
const getHeaders = async () => {
return {
"X-Auth-Token": token,
};
};
// functions has more content here...
return Promise.resolve({
link: errorLink.concat(link),
cache: new InMemoryCache(),
});
}
@NgModule({
imports: [HttpLinkModule],
providers: [
{
provide: APOLLO_OPTIONS,
useFactory: createApollo,
deps: [HttpLink, FirebaseService],
},
],
})
export class DgraphModule { }
问题是它解决了async函数,但在返回之前没有解决。我在StackOverFlow上看到了其他一些帖子来解决这个问题,但它们最终忽略了函数的async部分。你不能有一个不在异步函数中的等待,所以我在这里没有注意到。
如果异步函数不是异步函数,您也不能将异步函数放入异步函数中。。。那我在这里做什么?
更新:2/11/21-根据这一点,有一种方法可以用PlatformBrowserDynamic()
来实现,但我不知道如何在我的模块中实现它。
更新:2/13/21这是指向codeandbox.io的链接-忽略缺少html或工作端点,但您可以查看模块并将其更改为async
函数,以查看是否存在不变违规错误--->请确保查看代码和机顶盒控制台中是否存在错误。
依赖项注入中不能有异步工厂函数,这是不受支持的。有一个内置的功能可以帮助您解决这个问题,称为应用程序初始化程序。您可以使用内置的APP_initializer令牌在依赖项注入中注册应用程序初始值设定项。此令牌需要一个工厂,该工厂将返回一个函数,该函数将被调用为应用程序初始值设定项,并且它可以返回promise。这个应用程序初始化程序在应用程序开始时被调用,直到所有操作完成,应用程序才会启动。对于您的情况,我认为这是可以的,因为您阅读了某种配置。不幸的是,据我所知,没有关于这方面的好的官方文件。如果你搜索,你会发现一些关于这个主题的文章。这里有一个链接,指向另一个解释它的堆栈溢出问题。要在您的情况中使用这种方法,我认为最好创建一个进行初始化并保持值的服务。
@Injectable()
export class ApolloOptionsService {
public apolloOptions: any;
constructor(private httpLink: HttpLink) {}
public createApollo() {
const token = "";
const getHeaders = async () => {
return {
"X-Auth-Token": token
};
};
const http = ApolloLink.from([
setContext(async () => {
return {
headers: await getHeaders()
};
}),
this.httpLink.create({
uri: `https://${endpoint}`
})
]);
// Create a WebSocket link:
const ws = new WebSocketLink({
uri: `wss://${endpoint}`,
options: {
reconnect: true,
connectionParams: async () => {
return await getHeaders();
}
}
});
const link = split(
// split based on operation type
({ query }) => {
const definition = getMainDefinition(query);
return (
definition.kind === "OperationDefinition" &&
definition.operation === "subscription"
);
},
ws,
http
);
return new Promise((resolve) => {
setTimeout(() => {
resolve({
link: link,
cache: new InMemoryCache()
});
}, 5000);
}).then((apoloOptions) => (this.apolloOptions = apoloOptions));
}
}
正如你所看到的,我在承诺中也添加了一个延迟,以模拟它稍后完成。现在,让我们还更改您的模块以使用应用程序初始值设定项。
export function createApollo(apolloOptionsService: ApolloOptionsService) {
return () => apolloOptionsService.createApollo();
}
export function getApolloOptions(apolloOptionsService: ApolloOptionsService) {
return apolloOptionsService.apolloOptions;
}
@NgModule({
imports: [HttpLinkModule],
providers: [
ApolloOptionsService,
{
provide: APP_INITIALIZER,
useFactory: createApollo,
deps: [ApolloOptionsService],
multi: true
},
{
provide: APOLLO_OPTIONS,
useFactory: getApolloOptions,
deps: [ApolloOptionsService]
}
]
})
export class GraphQLModule {}
正如您所看到的,我们使用一个工厂函数来创建应用程序初始化器函数,该函数在应用程序初始化时被调用。需要另一个工厂函数从服务中读取初始化值,并将其作为APOLLO_OPTIONS
令牌提供。因为初始化程序是在应用程序启动之前运行的,所以值在使用之前就已经准备好了。
我还创建了一个代码沙盒的分支,您可以在其中看到它的实际操作。
因此,
我在这里的具体用例是获得一个包含环境键值对的JSON文件,所有这些当前都会在适当的地方产生新的ApolloLinks。现在,我们有一个";终止";链接和两个用于订阅。我们不只是使用Angular提供的环境文件的原因是,我希望能够在我们的CI过程中设置这些值,这意味着我们将方便输入一次,然后可以在任何地方构建。在所有这些变化中,我花了一到两个小时,我们立即以这种方式扩展到4个环境。
然而,自从我引入了这种设置变量的方法以来,我需要一些方法来阻止GraphQL客户端在它们被知道之前被设置。因此,在此之前,唯一的方法是在main.ts文件中获取JSON文件,将JSON响应保存到窗口,然后在相同的.then回调中引导应用程序。
然后在apollo角度设置中,我访问窗口上的全局变量,读出我想要的值,然后在返回函数之前从窗口中删除全局变量。我导入了ApolloModule,然后将Factory-d使用到APOLO_OPTIONS令牌中。
我已经断断续续地摆弄了三天了。我尝试了上面公认的答案,但始终得到了";客户端尚未定义";错误,或关于丢失缓存属性的错误,当您根本没有设置APOLO_OPTIONS令牌时会得到这些错误。直到今天,我才真正找到了解决这个问题的方法:可以异步处理这个过程
要启动,请不要导入ApolloModule。此模块需要APOLO_OPTIONS令牌,并且不能异步设置此令牌值。
接下来,不提供APOLO_OPTIONS,而是提供APP_INITIALIZER。将HttpLink和Apollo都设置为其依赖项。对您在同一文件中声明的函数调用useFactory。
从这个函数中,调用异步设置函数然后将.Then链接到apollo.create()中,将返回的配置传递到.create)方法中
您的异步设置函数就是:一个异步函数,在其中您调用await fetch(),然后调用await.json(),再设置所有链接、缓存和选项。返回您的最终配置对象,这样您就可以将其链接到apollo.create()中。真是太好了!
粗糙的代码示例,我无法分享完整的来源:
import { HttpClientModule } from '@angular/common/http';
import { APP_INITIALIZER, NgModule } from '@angular/core';
import { Apollo } from 'apollo-angular';
const setup = async (httpLink: HttpLink) => {
const response = await fetch('');
const data = await response.json();
// rest of your setup
return { links, cache, defaultOptions };
}
const factoryFn = (httpLink: HttpLink, apollo: Apollo) =>
setup(httpLink)
.then(config => apollo.create(config))
.catch(e => console.error(e));
@NgModule({
imports: [HttpClientModule],
providers: [
Apollo,
{
provide: APP_INITIALIZER,
useFactory: factoryFn,
deps: [HttpLink, Apollo]
}
]
})
export class ModuleNameHere {}
因此,它的长短:
如果要异步设置Apollo客户端,请不要使用APOLO_OPTIONS。它总是同步运行,APP_INITIALIZER不会阻止它