Hi everyone, today I want to introduce you to an innovative and young library that I consider very stable thanks to the experience I had with Relay in creating react-relay-offline and relay-hooks libraries.
大家好,今天想给大家介绍一下。一个我认为非常稳定的创新年轻图书馆,归功于我在Relay上创建React-Relay-Offline和Relay-hooks库的经验。
I admit I’m not Angular’s biggest fan but creating this library allowed me to appreciate many of its aspects and realize that Relay and Angular are a great match!
我承认我不是Angular最大的粉丝,但是创建这个库让我欣赏它的很多方面,意识到它Relay和Angular是很好的搭配!
Let’s start with a brief description of the three main components:
让我们从三个主要组成部分的简要描述开始:
角度的 (Angular)
()
Angular is a platform and framework for building single-page client applications using HTML and TypeScript. Angular is written in TypeScript. It implements core and optional functionality as a set of TypeScript libraries that you import into your apps.
Angular是用于使用HTML和TypeScript构建单页客户端应用程序的平台和框架。 Angular用TypeScript编写。 作为一组导入应用程序的核心和可选功能TypeScript库。
中继 (Relay)
生产就绪的GraphQL客户端。(The production-ready GraphQL client.)
Relay is a JavaScript framework for building data-driven React applications powered by GraphQL, designed from the ground up to be easy to use, extensible and, most of all, performant. Relay accomplishes this with static queries and ahead-of-time code generation.
Relay是一个JavaScript框架用于构框架GraphQL支持数据驱动React从头开始设计应用程序,易于使用,可扩展,最重要的是性能高。 该任务由静态查询和提前代码生成中继完成。
Relay Modern is composed of three core modules and one babel plugin:
Relay Modern由三个核心模块和一个babel插件组成:
A GraphQL to GraphQL optimizing compiler, providing general utilities for transforming and optimizing queries as well as generating build artifacts. A novel feature of the compiler is that it facilitates experimentation with new GraphQL features — in the form of custom directives — by making it easy to translate code using these directives into standard, spec-compliant GraphQL.
从GraphQL到GraphQL的优化编译器,提供一般实用程序,用于转换和优化查询,生成构造工件。 编译器的新功能是通过使用自定义指令轻松将代码转换为标准和标准GraphQL,从而简化了GraphQL新功能试验。
A full-featured, high-performance GraphQL runtime that can be used to build higher-level client APIs. The runtime features a normalized object cache, optimized “write” and “read” operations, a generic abstraction for incrementally fetching field data (such as for pagination), garbage collection for removing unreferenced cache entries, optimistic mutations with arbitrary logic, support for building subscriptions and live queries, and more.
功能齐全的高性能GraphQL运行时,可用于构建更高层次的客户端API。 在运行过程中,有标准化的对象缓存、优化的写入和读取操作,用于增量字段数据(如分页)的通用抽象,删除未引用的缓存项目的垃圾收集,任何逻辑的乐观突变,支持订阅和实时查询。
A high-level product API that integrates the Relay Runtime with React. This is the primary public interface to Relay for most product developers, featuring APIs to fetch the data for a query or define data dependencies for reusable components (aka containers).
一种高级产品API ,将Relay Runtime与React集成在一起。 对于大多数产品开发人员来说,这是Relay主要的公共接口API定义数据相关性,以获取查询数据或可重复使用的组件(也称为容器)。
a Babel plugin to convert GraphQL to runtime artifacts
Babel插件,可将GraphQL转换为运行时的工件
Note that these modules are loosely coupled. For example, the compiler emits representations of queries in a well-defined format that the runtime consumes, such that the compiler implementation can be swapped out if desired. relies only on the well-documented public interface of the runtime, such that the actual implementation can be swapped out (in fact, we've upgraded the classic Relay core to also implement this same API). use-cases such as the development of specialized product APIs using the Relay runtime or
请注意,这些模块是松散耦合的。 例如,编译器以运行时使用的定义明确的格式发出查询的表示形式,以便在需要时可以调出编译器实现。 仅依赖于运行时记录良好的公共接口,以便可以替换实际的实现(实际上,我们已经升级了经典的Relay核心,以实现相同的API)。 用例,例如使用Relay运行时开发专用产品API或
中继角度 (Relay Angular)
Angular的可量产的GraphQL客户端。(The production-ready GraphQL client for Angular.)
Now, I can confirm that this loosely coupling allowed me an easy and solid integration with Angular.
现在,我可以确认这种松散的耦合使我可以轻松,牢固地与Angular集成。
The Relay Team hopes that this loose coupling will allow the community to explore new integrations of the runtime with view libraries other than React.
中继团队希望这种松散的耦合将使社区能够使用React以外的视图库探索运行时的新集成。
This is done by creating a new core module to replace relay / react. Furthermore, it was necessary to create a new plugin as Angular does not officially support babel.
这是通过创建一个新的核心模块来代替继电器/React来完成的。 此外,由于Angular不正式支持babel,因此有必要创建一个新插件。
A high-level product API that integrates the Relay Runtime with Angular.
将Relay Runtime和Angular集成在一起的高级产品API 。
This plugin is the equivalent of the babel plugin for relay but built using the ngx-build-plus library which allows you to extend the Angular CLI’s default build behavior without ejecting. Its realization was necessary as Angular does not officially support babel.
此插件与babel插件等效,但使用ngx-build-plus库构建,该库允许您扩展Angular CLI的默认构建行为而无需弹出。 由于Angular并未正式支持babel,因此必须意识到这一点。

入门(Getting started)
First, let’s install the packages we need:
首先,让我们安装所需的软件包:
- relay-angular using yarn or npm: 使用yarn或npm中继角:
yarn add relay-angular relay-runtime
- relay-angular-plugin & relay-compiler using yarn or npm: 使用yarn或npm的中继角插件和中继编译器:
yarn add relay-angular-plugin ngx-build-plus relay-compiler relay-config
1.配置编译器 (1. Configure Compiler)
配置中继(Configure relay)
Here you will find the official documentation on how to configure relay compiler.
在这里,您将找到有关如何配置中继编译器的官方文档。
Example relay configuration file:
中继配置文件示例:
module.exports = {
// ...
// Configuration options accepted by the `relay-compiler` command-line tool, `babel-plugin-relay` and `relay-angular-plugin`.
src: './src',
schema: '../server/data/schema.graphql',
language: 'typescript',
artifactDirectory: './src/__generated__/relay/',
};
配置ngx-build-plus(Configure ngx-build-plus)
see ngx-build-plus getting started
查看ngx-build-plus入门
- angular.jsonangular.json
Change the builder to serve and build
更改构建者以服务和构建
{
"build": {
"builder": "ngx-build-plus:browser",
...
},
"serve": {
"builder": "ngx-build-plus:dev-server",
...
}
}
配置package.json(Configure package.json)
"scripts": {
...
"build": "ng build --plugin relay-angular-plugin",
"start": "ng serve --plugin relay-angular-plugin",
"compile": "relay-compiler"
...
}
2.配置中继运行时(2. Configure Relay Runtime)
Here you will find the official documentation on how to configure relay runtime.
在这里,您将找到有关如何配置中继运行时的官方文档。
3.将中继运行时连接到Angular (3. Connect Relay Runtime to Angular)
You connect Relay Runtime to Angular with the RelayProvider
provider. The RelayProvider
wraps your Angular app and places the environment in the context, which enables you to access it from anywhere.
使用RelayProvider
提供程序将Relay Runtime连接到Angular。 RelayProvider
包装了Angular应用程序并将环境放置在上下文中,这使您可以从任何地方访问它。
中继提供者 (RelayProvider)
RelayProvider
takes an environment
and sets it in the context.
RelayProvider
采用environment
并将其设置在上下文中。
import { RelayProvider } from 'relay-angular';
import { Environment, Network, RecordSource, Store } from 'relay-runtime';
async function fetchQuery(operation, variables, cacheConfig, uploadables) {
const response = await fetch('http://localhost:3000/graphql', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
query: operation.text,
variables,
}),
});
return response.json();
}
const modernEnvironment: Environment = new Environment({
network: Network.create(fetchQuery),
store: new Store(new RecordSource()),
});
@NgModule({
declarations: [
...
],
imports: [
...
],
providers: [[RelayProvider(modernEnvironment)]],
bootstrap: [AppComponent]
})
export class AppModule {
}
If you want change environment in your Angular app use:
如果要在Angular应用中更改环境,请使用:
环境语境 (EnvironmentContext)
EnvironmentContext is an extension of BehaviorSubject from rxjs.
EnvironmentContext是rxjs的BehaviorSubject的扩展。
import { Component } from '@angular/core';
import { EnvironmentContext } from 'relay-angular';
import EnvironmentError from '../relay/errorRelay';
import EnvironmentRight from '../relay/relay';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
})
export class AppComponent {
constructor(private environmentContext: EnvironmentContext) {}
// handle click button
handleRightEnv() {
this.environmentContext.next(EnvironmentRight);
}
// handle click button
handleWrongEnv() {
this.environmentContext.next(EnvironmentError);
}
}
4.使用Relay作为Angular装饰器(4. Use Relay as Angular decorator)
@查询(@Query)
Decorator used to fetch a GraphQL query. It does not take an environment as an argument. Instead, it reads the environment set in the context; In addition to query
(first argument) and variables
(second argument), @query
accepts a third argument options
.
用于获取GraphQL查询的装饰器。 它不以环境为参数。 相反,它读取上下文中设置的环境。 除了query
(第一个参数)和variables
(第二个参数)之外, @query
接受第三个参数options
。
query
: The graphql
tagged query. relay-compiler
enforces the query to be named as <FileName>Query
. [Optional], if not provided, an empty props
object is returned.
query
: graphql
标记的查询。 relay-compiler
强制将查询命名为<FileName>Query
。 [可选],如果未提供,则返回一个空的props
对象。
variables:
Object containing set of variables to pass to the GraphQL query, i.e. a mapping from variable name to value. If a new set of variables is passed, the @Query
will re-fetch the query.
variables:
包含要传递给GraphQL查询的变量集的对象,即从变量名到值的映射。 如果传递了一组新的变量,则@Query
将重新获取查询。
options:
see below
options:
见下文
fetchPolicy
: determine whether it should use data cached in the Relay store and whether to send a network request. The options are:
fetchPolicy
:确定它是否应该使用中继存储中缓存的数据以及是否发送网络请求。 选项包括:
store-or-network
(default): Reuse data cached in the store; if the whole query is cached, skip the network requeststore-or-network
(默认):重用存储在存储中的数据; 如果整个查询都已缓存,请跳过网络请求store-and-network
: Reuse data cached in the store; always send a network request.store-and-network
:重用存储在存储中的数据; 始终发送网络请求。network-only
: Don't reuse data cached in the store; always send a network request. (This is the default behavior of Relay's existing@Query
.)network-only
:请勿重复使用存储在缓存中的数据; 始终发送网络请求。 (这是Relay现有的@Query
的默认行为。)store-only
: Reuse data cached in the store; never send a network request.store-only
:重复使用存储在存储中的数据; 从不发送网络请求。
fetchKey
: [Optional] A fetchKey can be passed to force a refetch of the current query and variables when the component re-renders, even if the variables didn't change, or even if the component isn't remounted (similarly to how passing a different key to a React component will cause it to remount). If the fetchKey is different from the one used in the previous render, the current query and variables will be refetched.
fetchKey
:[可选]可以在组件重新渲染时传递fetchKey,以强制重新查询当前查询和变量,即使变量没有变化,或者即使没有重新安装组件也是如此(类似于传递使用React组件的其他键会导致其重新安装)。 如果fetchKey与上一个渲染中使用的fetchKey不同,则将重新获取当前查询和变量。
networkCacheConfig
: [Optional] Object containing cache config options for the network layer. : the network layer may contain an additional query response cache which will reuse network responses for identical queries. If you want to bypass this cache completely, pass {force: true} as the value for this option.
networkCacheConfig
:[可选]包含网络层的缓存配置选项的对象。 :网络层可能包含一个附加的查询响应缓存,它将对相同的查询重用网络响应。 如果要完全绕过此缓存,请传递{force:true}作为此选项的值。
skip
: [Optional] If skip is true, the query will be skipped entirely
skip
:[可选]如果skip为true,则将完全跳过查询
props
: Object containing data obtained from the query; the shape of this object will match the shape of the query. If this object is not defined, it means that the data is still being fetched.props
:包含从查询获得的数据的对象; 该对象的形状将与查询的形状匹配。 如果未定义此对象,则意味着仍在获取数据。error
: Error will be defined if an error has occurred while fetching the query.error
:如果在获取查询时发生错误,则将定义错误。retry
: Function for reloading the dataretry
:用于重新加载数据的功能
import { Component, Input } from '@angular/core';
import { graphql } from 'relay-runtime';
import { Query, RenderProps } from 'relay-angular';
import { todoQueryQuery } from '../../__generated__/relay/todoQueryQuery.graphql';
export const QueryApp = graphql`
query todoQueryQuery($userId: String) {
user(id: $userId) {
id
...todoApp_user
}
}
`;
@Component({
selector: 'todo-query',
templateUrl: './todo-query.component.html',
styleUrls: ['./todo-query.component.css'],
})
export class TodoQueryComponent {
@Input()
userId;
@Query<todoQueryQuery>(function() {
return {
query: QueryApp,
variables: { userId: this.userId },
};
})
result: RenderProps<todoQueryQuery>;
}
<todo-app *ngIf="result && result.props && result.props.user; else loading"
[fragmentRef]="result.props.user">
{
{result}}
</todo-app>
<ng-template #loading>
<div *ngIf="!result.error; else error">
Loading...</div>
</ng-template>
<ng-template #error>
<div>
Error {
{ result.error }}
</div>
</ng-template>
@分段(@Fragment)
@fragment
allows components to specify their data requirements. A container does not directly fetch data, but instead declares a specification of the data needed for rendering, and then Relay will guarantee that this data is available before rendering occurs.
@fragment
允许组件指定其数据要求。 容器不直接获取数据,而是声明渲染所需数据的规范,然后Relay将保证在渲染发生之前该数据可用。
The decorator is automatically subscribed to updates to the fragment data: if the data for this particular User
is updated anywhere in the app (e.g. via fetching new data, or mutating existing data), the component will automatically re-render with the latest updated data.
装饰器自动订阅片段数据的更新:如果此特定User
的数据在应用程序中的任何位置进行了更新(例如,通过获取新数据或更改现有数据),该组件将自动使用最新的更新数据进行渲染。 。
:
:
fragment
: GraphQL fragment specified using agraphql
template literal.fragment
:使用graphql
模板文字指定的GraphQL片段。fragmentReference
: The is an opaque Relay object that Relay uses to read the data for the fragment from the store; more specifically, it contains information about which particular object instance the data should be read from. of the fragment reference can be imported from the generated Flow/Typescript types, from the file<fragment_name>.graphql.js
, and can be used to declare the type of yourProps
. The name of the fragment reference type will be:<fragment_name>$key
.fragmentReference
: 是一个不透明的Relay对象,Relay使用该对象从商店读取该片段的数据。 更具体地说,它包含有关应从中读取数据的特定对象实例的信息。 片段引用可以从生成的Flow / Typescript类型,从文件<fragment_name>.graphql.js
,并且可以用来声明Props
的类型。 片段引用类型的名称为:<fragment_name>$key
。
data
: Object that contains data which has been read out from the Relay store; the object matches the shape of specified fragment.data
:包含已从中继存储中读取的data
对象; 对象与指定片段的形状匹配。
import { Component, Input } from '@angular/core';
import { Fragment } from 'relay-angular';
import { graphql } from 'relay-runtime';
import { todoListItem_todo$key, todoListItem_todo$data } from '../../__generated__/relay/todoListItem_todo.graphql';
const fragmentNode = graphql`
fragment todoListItem_todo on Todo {
complete
id
text
}
`;
@Component({
selector: 'app-todo-list-item',
templateUrl: './todo-list-item.component.html',
styleUrls: ['./todo-list-item.component.css'],
})
export class TodoListItemComponent {
@Input()
fragmentRef: todoListItem_todo$key;
@Fragment<todoListItem_todo$key>(function() {
return {
fragmentNode,
fragmentRef: this.fragmentRef,
};
})
todo: todoListItem_todo$data;
}
<div class="view">
<input class="toggle" type="checkbox" (click)="toggleTodoComplete()" [checked]="todo.complete">
<label>{
{todo.text}}</label>
<button class="destroy" (click)="removeTodo(todo)"></button>
</div>
@Refetch(@Refetch)
You can use @refetch
when you want to fetch and re-render a fragment with different data.
当您要获取并重新渲染具有不同数据的片段时,可以使用@refetch
。
They are the same as @fragment
.
它们与@fragment
相同。
data
: Same object as @fragment
but with the addition of the refetch
key, function used to refetch the fragment with a potentially new set of variables.
data
:与@fragment
相同的对象,但添加了refetch
键,该函数用于使用一组潜在的新变量来重新片段。
import { Component, Input } from '@angular/core';
import { Refetch, RefetchDecorator } from 'relay-angular';
import { graphql } from 'relay-runtime';
import { todoListFooter_user$data } from '../../__generated__/relay/todoListFooter_user.graphql';
import { QueryApp } from '../todo-query/todo-query.component';
const fragmentNode = graphql`
fragment todoListFooter_user on User {
id
userId
completedCount
todos(
first: 2147483647 # max GraphQLInt
) @connection(key: "TodoList_todos") {
edges {
node {
id
complete
}
}
}
totalCount
}
`;
@Component({
selector: 'app-todo-list-footer',
templateUrl: './todo-list-footer.component.html',
styleUrls: ['./todo-list-footer.component.css'],
})
export class TodoListFooterComponent {
@Input()
fragmentRef: any;
@Refetch((_this) => ({
fragmentNode,
fragmentRef: _this.fragmentRef,
}))
data: RefetchDecorator<todoListFooter_user$data>;
// handle button click
handleRefresh() {
const { refetch, userId } = this.data;
refetch(QueryApp, {
userId,
});
}
}
<footer class="footer">
<span class="todo-count">
<label>{
{data.totalCount - data.completedCount}}</label>
{
{data.totalCount - data.completedCount == 1 ? 'item' : 'items'}} left
</span>
<button
class="clear-completed"
(click)="handleRefresh($event)">
Refresh
</button>
</footer>
@分页(@Pagination)
You can use @Pagination
to render a fragment that uses a @connection
and paginate over it.
您可以使用@Pagination
渲染使用@connection
的片段并对其进行分页。
They are the same as useFragment.
它们与useFragment相同。
data
: Same object as @fragment
but with the addition of the keys: loadMore
, hasMore
, isLoading
, refetchConnection
data
:与@fragment
相同的对象,但具有以下按键: loadMore
, hasMore
, isLoading
, refetchConnection
有更多 (hasMore)
This function indicates whether there are more pages to fetch from the server or not.
此功能指示是否还有更多页面要从服务器获取。
hasMore: (connectionConfig?: ConnectionConfig) => boolean,
connectionConfig
[Optional] :
connectionConfig
[可选]:
direction
: Either "forward" to indicate forward pagination using after/first, or "backward" to indicate backwards pagination using before/last. If not provided, Relay will infer the direction based on the provided@connection
directive.direction
:“ forward”使用后/首指示前向分页,或者“ backward”使用前/后指示后向分页。 如果未提供,则Relay将根据提供的@connection
指令推断方向。getConnectionFromProps
: Function that should indicate which connection to paginate over, given the fragment props (i.e. the props corresponding to thefragmentSpec
). This is necessary in most cases because the Relay can't automatically tell which connection you mean to paginate over (a container might fetch multiple fragments and connections, but can only paginate one of them). If not provided, Relay will try infer the correct connection to paginate over based on the provided@connection
directive.getConnectionFromProps
:功能应该指示分页在其上连接,给定片段的道具(即,对应于道具fragmentSpec
)。 这在大多数情况下是必需的,因为中继无法自动告诉您要分页的连接(容器可能会提取多个片段和连接,但只能分页其中之一)。 如果未提供,则Relay将尝试根据提供的@connection
指令推断正确的连接进行分页。getFragmentVariables
: Function that should return the bag of variables to use for reading out the data from the store when re-rendering the component. This function takes the previous set of variables passed to the paginationquery
, and the number of elements that have been fetched in total so far. Specifically, this indicates which variables to use when reading out the data from the local data store after the new paginationquery
has been fetched. If not specified, Relay will default to using all of the previous variables and using the total count for thecount
variable.getFragmentVariables
:该函数应返回一袋变量,用于在重新呈现组件时从存储中读取数据。 此函数采用传递给分页query
的上一组变量,以及到目前为止总共已获取的元素数。 具体来说,这表示在获取新的分页query
之后从本地数据存储中读取数据时要使用哪些变量。 如果未指定,则Relay将默认使用所有先前的变量,并使用count
变量的总计数。getVariables
: Function that should return the variables to pass to the paginationquery
when fetching it from the server, given the currentprops
,count
andcursor
. You may set whatever variables here, as well as modify the defaults to use for after/first/before/last arguments.getVariables
:在给定当前props
,count
和cursor
,从服务器获取分页query
时应返回变量以传递给分页query
函数。 您可以在此处设置任何变量,也可以修改默认值以用于after / first / before / last参数。query
: Agraphql
tagged query to be used as the pagination query to fetch more data upon callingloadMore
.query
:一个graphql
标记的查询,用作分页查询,以在调用loadMore
时获取更多数据。
isLoading (isLoading)
This function indicates if a previous call to loadMore()
is still pending. This is convenient for avoiding duplicate load calls.
此函数指示是否仍未处理对loadMore()
的先前调用。 这对于避免重复的加载调用很方便。
isLoading: () => boolean,
装载更多 (loadMore)
You can call loadMore()
to fetch more items from the server based on the connectionConfig
provided to the container. This will return null if there are no more items to fetch, otherwise it will fetch more items and return a Disposable that can be used to cancel the fetch.
您可以调用loadMore()
以根据提供给容器的connectionConfig
从服务器获取更多项目。 如果没有更多要提取的项目,则将返回null,否则它将获取更多的项目并返回可用于取消提取的Disposable。
loadMore( connectionConfig: ConnectionConfig, pageSize: number, callback: ?(error: ?Error) => void, options?: RefetchOptions): ?Disposable
connectionConfig
[required]: Same as hasMore function
connectionConfig
[必需]:与hasMore函数相同
pageSize
: The number of items to fetch (not the total).
pageSize
:要提取的项目数(不是总数)。
callback
: Function called when the new page has been fetched. If an error occurred during refetch, this function will receive that error as an argument.
callback
:获取新页面时callback
函数。 如果在重新提取过程中发生错误,则此函数将接收该错误作为参数。
options
: Optional object containing set of options.
options
:包含选项集的可选对象。
force
: If the Network Layer has been configured with a cache, this option forces a refetch even if the data for this query and variables is already available in the cache.
force
:如果网络层已配置了缓存,则即使此查询和变量的数据已在缓存中,此选项也会强制进行重新提取。
refetchConnection (refetchConnection)
You can call refetchConnection
to restart pagination on a connection from scratch, with optionally a completely new set of variables to pass to the pagination query
. This is useful for example if you are paginating over a collection based on a userID and the userID changes, you'd want to start paginating over the new collection for the new user.
您可以调用refetchConnection
从头开始在连接上重新启动分页,并可以选择使用一组全新的变量传递给分页query
。 例如,如果您要基于用户ID对集合进行分页并且用户ID发生变化,而您想开始为新用户对新集合进行分页,则这很有用。
refetchConnection:( callback: (error: ?Error) => void, refetchVariables: ?Variables,) => ?Disposable,
connectionConfig
[required]: Same as hasMore function
connectionConfig
[必需]:与hasMore函数相同
totalCount
: The total number of elements to fetch
totalCount
:要获取的元素总数
callback
: Function called when the new page has been fetched. If an error occurred during refetch, this function will receive that error as an argument.
callback
:获取新页面时callback
函数。 如果在重新提取过程中发生错误,则此函数将接收该错误作为参数。
refetchVariables
: A potentially new bag of variables to pass to the pagination query
when fetching it from the server.
refetchVariables
:从服务器获取分页query
时可能会传递给分页query
的潜在新变量。
import { Component, Input } from '@angular/core';
import { Pagination, PaginationDecorator } from 'relay-angular';
import { graphql } from 'relay-runtime';
import { todoListFooter_user$data } from '../../__generated__/relay/todoListFooter_user.graphql';
import { QueryApp } from '../todo-query/todo-query.component';
const fragmentSpec = graphql`
fragment todoListFooter_user on User {
id
idUser
todos(idUser: $idUser, first: $first, after: $after)
@connection(key: "TodoList_todos", filters: ["idUser"]) {
nextToken
edges {
node {
id
complete
}
}
pageInfo {
hasNextPage
endCursor
}
}
}
`;
const connectionConfig = {
query: QueryApp,
getVariables: (props, paginationInfo) => ({
first: 2,
after: paginationInfo.cursor,
idUser: props.idUser
})
};
@Component({
selector: 'app-todo-list-footer',
templateUrl: './todo-list-footer.component.html',
styleUrls: ['./todo-list-footer.component.css'],
})
export class TodoListFooterComponent {
@Input()
fragmentRef: any;
@Pagination((_this) => ({
fragmentNode,
fragmentRef: _this.fragmentRef,
}))
data: PaginationDecorator<todoListFooter_user$data>;
// handle button click
handleLoadMore() {
const { hasMore, isLoading, loadMore } = this.data;
hasMore() && !isLoading() && loadMore(connectionConfig, 2, () => null, undefined)
}
}
<footer class="footer">
...
<button
class="clear-completed"
(click)="handleLoadMore()">
Load More
</button>
</footer>
@RelayEnvironment(@RelayEnvironment)
Decorator used to access a Relay environment that was set by a RelayProvider
:
用于访问由RelayProvider
设置的Relay环境的RelayProvider
:
import { Component } from '@angular/core';
import { RelayEnvironment } from 'relay-angular';
import { graphql, Environment } from 'relay-runtime';
@Component({
selector: 'todo-app',
templateUrl: './todo-app.component.html',
styleUrls: ['./todo-app.component.css'],
})
export class TodoAppComponent {
@RelayEnvironment()
environment: Environment;
}
4.变异(4. Mutation)
Use mutate
function to create and execute mutations. mutate
has the following signature:
使用mutate
函数创建和执行变异。 mutate
具有以下签名:
options:
see below
options:
见下文
mutation
: Thegraphql
tagged mutation query.mutation
:graphql
标记的突变查询。variables
: Object containing the variables needed for the mutation. For example, if the mutation defines an$input
variable, this object should contain aninput
key, whose shape must match the shape of the data expected by the mutation as defined by the GraphQL schema.variables
:包含突变所需variables
对象。 例如,如果变异定义了$input
变量,则此对象应包含一个input
键,其形状必须与GraphQL架构定义的变异所期望的数据形状匹配。onCompleted
: Callback function executed when the request is completed and the in-memory Relay store is updated with theupdater
function. Takes aresponse
object, which is the updated response from the store, anderrors
, an array containing any errors from the server.onCompleted
:当请求完成并且使用updater
功能更新内存中的中继存储时执行的回调函数。 获取一个response
对象,该对象是来自商店的更新后的响应,以及一个errors
,一个包含服务器中所有错误的数组。onError
: Callback function executed if Relay encounters an error during the request.onError
:如果中继在请求期间遇到错误,则执行回调函数。optimisticResponse
: Object containing the data to optimistically update the local in-memory store, i.e. immediately, before the mutation request has completed. This object must have the same shape as the mutation's response type, as defined by the GraphQL schema. If provided, Relay will use theoptimisticResponse
data to update the fields on the relevant records in the local data store, beforeoptimisticUpdater
is executed. If an error occurs during the mutation request, the optimistic update will be rolled back.optimisticResponse
:包含数据的对象,用于乐观地更新本地内存存储,即在变异请求完成之前立即更新。 该对象必须具有与GraphQL架构定义的突变的响应类型相同的形状。 如果提供,接力将使用optimisticResponse
数据以更新相关记录的字段在本地数据存储,之前optimisticUpdater
执行。 如果在变异请求期间发生错误,乐观更新将被回滚。optimisticUpdater
: Function used to optimistically update the local in-memory store, i.e. immediately, before the mutation request has completed. If an error occurs during the mutation request, the optimistic update will be rolled back. This function takes astore
, which is a proxy of the in-memory Relay Store. In this function, the client defines 'how to' update the local data via thestore
instance. For details on how to use thestore
, please refer to our Relay Store API Reference. It is usually preferable to just pass anoptimisticResponse
option instead of anoptimisticUpdater
, unless you need to perform updates on the local records that are more complicated than just updating fields (e.g. deleting records or adding items to collections). If you do decide to use anoptimisticUpdater
, often times it can be the same function asupdater
.optimisticUpdater
:用于乐观地更新本地内存存储的功能,即在变异请求完成之前立即更新。 如果在变异请求期间发生错误,乐观更新将被回滚。 此功能接受一个store
,该store
是内存中的中继存储的代理。 在此功能中,客户端通过store
实例定义“如何”更新本地数据。 有关如何使用store
详细信息,请参阅我们的中继商店API参考。 通常最好只传递optimisticResponse
选项而不是optimisticUpdater
,除非您需要对本地记录执行更新,而不仅仅是更新字段(例如,删除记录或向集合中添加项目)更复杂。 如果您决定使用optimisticUpdater
,通常它可以与updater
具有相同的功能。updater
: Function used to update the local in-memory store based on the server response from the mutation. Ifupdater
is not provided, by default, Relay will know to automatically update the fields on the records referenced in the mutation response; however, you should pass anupdater
if you need to make more complicated updates than just updating fields (e.g. deleting records or adding items to collections). When the server response comes back, Relay first reverts any changes introduced byoptimisticUpdater
oroptimisticResponse
and will then executeupdater
. This function takes astore
, which is a proxy of the in-memory Relay Store. In this function, the client defines 'how to' update the local data based on the server response via thestore
instance. For details on how to use thestore
, please refer to our Relay Store API Reference.updater
:用于根据来自突变的服务器响应来更新本地内存存储的功能。 如果未提供updater
则默认情况下,中继将知道自动更新变异响应中引用的记录上的字段; 但是,如果您需要进行的更新不仅限于更新字段(例如,删除记录或向集合中添加项目),还应通过updater
。 当服务器响应返回时,Relay首先还原由optimisticUpdater
或optimisticResponse
引入的所有更改,然后执行updater
。 此功能接受一个store
,该store
是内存中的中继存储的代理。 在此功能中,客户端根据服务器通过store
实例的响应来定义“如何”更新本地数据。 有关如何使用store
详细信息,请参阅我们的中继商店API参考。configs
: Array containing objects describingoptimisticUpdater
/updater
configurations.configs
provides a convenient way to specify theupdater
behavior without having to write anupdater
function. See our section on Updater Configs for more details.configs
:包含描述optimisticUpdater
/updater
配置的对象的数组。configs
提供了一种方便的方法来指定updater
行为,而无需编写updater
功能。 有关更多详细信息,请参见“更新程序配置”部分。cacheConfig?
: Optional object containing a set of cache configuration optionscacheConfig?
:包含一组缓存配置选项的可选对象
import { mutate } from 'relay-angular';
import { graphql } from 'relay-runtime';
export const mutation = graphql`
mutation changeTodoStatusMutation($input: ChangeTodoStatusInput!) {
changeTodoStatus(input: $input) {
todo {
id
complete
}
user {
id
completedCount
}
}
}
`;
function commit(complete: boolean, todo: any, user: any): any {
const input: any = {
complete,
userId: user.userId,
id: todo.id,
};
return mutate({
mutation,
variables: {
input,
},
});
}
export default { commit };
import { Component } from '@angular/core';
import changeTodoStatus from '../mutations/changeTodoStatus';
@Component({
selector: 'app-todo-list-item',
templateUrl: './todo-list-item.component.html',
styleUrls: ['./todo-list-item.component.css'],
})
export class TodoListItemComponent {
// handle button
toggleTodoComplete(todo, user) {
changeTodoStatus.commit(!todo.complete, todo, user);
}
}
结论:(Conclusions:)
This library simplifies the management of data fetching, offers a great developer experience and is completely open source!
该库简化了数据获取的管理,提供了出色的开发人员体验,并且是完全开源的!
If you think it can be improved please open some issues in the repository and we will discuss together!
如果您认为可以改进,请在存储库中打开一些问题,我们将一起讨论!
Well, if you’ve made it this far, you’re sure to be interested in seeing relay-angular in action!
好吧,如果您到目前为止已经做到了,那么您一定会对在实际中使用中继角感兴趣!
Try the sample project and tell me what you think.
尝试示例项目,并告诉我您的想法。
即将到来:(Cooming soon:)
Relay Angular for Offline-First Application
离线优先应用的中继角度
Render as you fetch for Angular
在获取Angular时渲染
翻译自: https://medium.com/@morrys/relay-for-angular-fbe8654e9132
机械继电器和固态继电器