vanilla
Complex web projects often require the use of 3rd party widgets. But what if you're using a framework while a widget is only available in pure JavaScript?
复杂的Web项目通常需要使用第三方控件。 但是,如果您使用的是框架,而小部件仅在纯JavaScript中可用,那该怎么办?
To use a JavaScript widget in your project, the best approach would be to create a framework specific wrapper. That's what this article is about.
要在项目中使用JavaScript小部件,最好的方法是创建特定于框架的包装器。 这就是本文的目的。
At ag-Grid we focus on developing the fastest and most feature rich JavaScript data grid for web applications. It has no 3rd party dependencies and is specifically designed to deliver outstanding performance even if being used to display millions of records. To make integrations with React applications easier, we've implemented our own React grid wrapper.
在ag-Grid,我们专注于为Web应用程序开发最快, 功能最丰富JavaScript数据网格 。 它没有第三方依赖性,并且专门用于提供出色的性能,即使用于显示数百万条记录也是如此。 为了简化与React应用程序的集成,我们实现了自己的React网格包装器。
In this article, I'll show you how to wrap a 3rd party widget into a React component using ag-Grid as an example. I'll show you how we set up the mapping between React Props and the widget's configuration options. You'll also learn how to expose a widget's API through a React component.
在本文中,我将向您展示如何使用ag-Grid作为示例将第3方小部件包装到React组件中 。 我将向您展示如何设置React Props与窗口小部件的配置选项之间的映射。 您还将学习如何通过React组件公开窗口小部件的API。
了解JS窗口小部件:定义要桥接的内容 ( Understanding the JS Widget: Defining things to bridge )
Most widgets can be configured through configuration options. Usually they also define public API and broadcasts events.
可以通过配置选项配置大多数窗口小部件。 通常,他们还定义公共API并广播事件。
In general, most widgets have:
通常,大多数小部件具有:
- Configuration options 配置选项
- A public API 公开的API
- Broadcasted events 广播事件
That's exactly how you interact with ag-Grid. You can find a good description for the grid's properties, events, callbacks and API here. In short, the datagrid defines:
这就是您与ag-Grid交互的方式。 您可以在此处找到有关网格的属性,事件,回调和API的良好描述。 简而言之,datagrid定义了:
Grid Properties that enable features of the grid, like row animation.
网格属性,可启用网格功能,例如行动画。
Grid API to interact with the grid at runtime, e.g. to get all the selected rows
网格API,可在运行时与网格进行交互,例如获取所有选定的行
Grid Events emitted by the grid when certain events happen in the grid, like row sorting or rows selection
网格中发生某些事件(例如行排序或行选择)时由网格发出的网格事件
Grid Callbacks used to supply information from your application to the grid when it needs it, e.g. a callback is called each time a menu is shown that allows your application to customize the menu.
网格回调用于在需要时从应用程序向网格提供信息,例如,每次显示菜单时都会调用一次回调,允许您的应用程序自定义菜单。
Here's a very basic pure JavaScript configuration that demonstrates the usage of grid options:
这是一个非常基本的纯JavaScript配置,演示了网格选项的用法:
let gridOptions = {
// PROPERTIES - object properties, myRowData and myColDefs are created somewhere in your application
rowData: myRowData,
columnDefs: myColDefs,
// PROPERTIES - simple boolean / string / number properties
pagination: true,
rowSelection: 'single',
// EVENTS - add event callback handlers
onRowClicked: function(event) { console.log('a row was clicked'); },
onColumnResized: function(event) { console.log('a column was resized'); },
onGridReady: function(event) { console.log('the grid is now ready'); },
// CALLBACKS
isScrollLag: function() { return false; }
}
Once the JavaScript data grid is initialized like this:
JavaScript数据网格初始化后,如下所示:
new Grid(this._nativeElement, this.gridOptions, ...);
ag-Grid
attaches the object with API methods to the gridOptions
that can be used to control the JavaScript data grid:
ag-Grid
将带有API方法的对象附加到gridOptions
,该gridOptions
可用于控制JavaScript数据网格:
// get the grid to refresh
gridOptions.api.refreshView();
However, when ag-Grid is used as a React component, we don't instantiate the datagrid directly. That's the job of the wrapper component.
但是,当将ag-Grid用作React组件时,我们不会直接实例化datagrid。 这就是包装器组件的工作。
All interactions with the instance of ag-Grid occurs through the component instance. For example, we don't have direct access to the API object attached by the grid. We will access it through the component's instance.
与ag-Grid实例的所有交互都通过组件实例发生。 例如,我们无法直接访问由网格附加的API对象。 我们将通过组件的实例访问它。
包装器组件应该做什么? ( What should a wrapper component do? )
We never pass configuration options and callbacks directly to the grid.
我们绝不会将配置选项和回调直接传递给网格。
A React wrapper component takes the options and callbacks through React Props.
React包装器组件通过React Props接受选项和回调。
All grid options that are available for vanilla JavaScript grid should be available in React datagrid as well. We also don't directly listen for events on the instance of ag-Grid. If we're using ag-Grid as a React component, all events emitted by ag-Grid should be available through React components props.
适用于原始JavaScript网格的所有网格选项也应该在React datagrid中可用。 我们也不直接在ag-Grid实例上监听事件。 如果我们使用ag-Grid作为React组件,则ag-Grid发出的所有事件都应该可以通过React组件props获得。
This all means that a React specific datagrid wrapper around ag-Grid should:
这一切都意味着,围绕ag-Grid的React专用数据网格包装器应该:
implement a mapping between input bindings (like rowData) and ag-Grid's configuration options.
实现输入绑定(例如rowData)和ag-Grid的配置选项之间的映射。
should listen for events emitted by ag-Grid and define them as component outputs
应该侦听ag-Grid发出的事件并将其定义为组件输出
listen for changes in component's input bindings and update configuration options in the grid
侦听组件输入绑定中的更改并更新网格中的配置选项
expose API attached by ag-Grid to the gridOptions through its properties
通过其属性公开由ag-Grid附加到gridOptions的API
The following example demonstrates how React datagrid is configured in a template using React Props:
以下示例演示了如何使用React Props在模板中配置React datagrid:
<AgGridReact
// useful for accessing the component directly via ref
ref="agGrid"
// these are simple attributes, not bound to any state or prop
rowSelection="multiple"
// these are bound props, so can use anything in React state or props
columnDefs={this.props.columnDefs}
showToolPanel={this.state.showToolPanel}
// this is a callback
isScrollLag={this.myIsScrollLagFunction.bind(this)}
// these are registering event callbacks
onCellClicked={this.onCellClicked.bind(this)}"
onColumnResized={this.onColumnEvent.bind(this)}"
onGridReady={this.onGridReady.bind(this)}" // inside onGridReady, you receive the grid APIs if you want them
/>
Now that we understand the requirement, let's see how we implemented it at ag-Grid
.
现在我们了解了需求,让我们看看如何在ag-Grid
实现它。
React包装器实施 ( React Wrapper Implementation )
First, we need to define a React component AgGridReact
that represents our React data grid in templates. This component will render a DIV
element that will serve as a container for the datagrid. To get a hold of the native DIV
element we use the Refs functionality:
首先,我们需要定义一个React组件AgGridReact
,该组件在模板中代表我们的React数据网格。 该组件将呈现一个DIV
元素,该元素将用作数据网格的容器。 为了掌握原生DIV
元素,我们使用Refs功能 :
export class AgGridReact extends React.Component {
protected eGridDiv: HTMLElement;
render() {
return React.createElement("div", {
style: ...,
ref: e => {
this.eGridDiv = e;
}
}, ...);
}
}
Before we can instantiate ag-Grid, we also need to collect all options. All ag-Grid properties and events come as React Props on the AgGridReact
component. The gridOptions
property is used to store all datagrid options. We need to copy all configuration options from React props as soon as they become available.
在实例化ag-Grid之前,我们还需要收集所有选项。 所有ag-Grid属性和事件AgGridReact
组件上的React Props的AgGridReact
。 gridOptions
属性用于存储所有数据网格选项。 一旦有可用,我们需要从React props复制所有配置选项。
To do that, we've implemented the copyAttributesToGridOptions
function. It's just a utility function that copies properties from one object to the other. Here's the gist of the function:
为此,我们实现了copyAttributesToGridOptions
函数。 它只是一个实用程序功能,可将属性从一个对象复制到另一个对象。 这是功能的要点:
export class ComponentUtil {
...
public static copyAttributesToGridOptions(gridOptions, component, ...) {
...
// copy all grid properties to gridOptions object
ComponentUtil.ARRAY_PROPERTIES
.concat(ComponentUtil.STRING_PROPERTIES)
.concat(ComponentUtil.OBJECT_PROPERTIES)
.concat(ComponentUtil.FUNCTION_PROPERTIES)
.forEach(key => {
if (typeof component[key] !== 'undefined') {
gridOptions[key] = component[key];
}
});
...
return gridOptions;
}
}
The options are copied in the componentDidMount
lifecycle method after all props have been updated. This is also the hook where we instantiate the grid. We need to pass a native DOM element to the data grid when it's being instantiated, so we'll use the DIV
element captured using refs functionality. Here's how it all looks:
在更新所有道具之后,将这些选项复制到componentDidMount
生命周期方法中。 这也是我们实例化网格的地方。 我们需要在实例化数据时将本机DOM元素传递到数据网格,因此我们将使用通过refs功能捕获的DIV
元素。 外观如下:
export class AgGridReact extends React.Component {
gridOptions: AgGrid.GridOptions;
componentDidMount() {
...
let gridOptions = this.props.gridOptions || {};
if (AgGridColumn.hasChildColumns(this.props)) {
gridOptions.columnDefs = AgGridColumn.mapChildColumnDefs(this.props);
}
this.gridOptions = AgGrid.ComponentUtil.copyAttributesToGridOptions(gridOptions, this.props);
new AgGrid.Grid(this.eGridDiv, this.gridOptions, gridParams);
this.api = this.gridOptions.api;
this.columnApi = this.gridOptions.columnApi;
}
}
You can see above that we also check if there are children that are passed as columns and add then to configuration options as column definitions:
您可以在上方看到我们还检查是否有子级作为列传递,然后将其添加到配置选项中作为列定义:
if (AgGridColumn.hasChildColumns(this.props)) {
gridOptions.columnDefs = AgGridColumn.mapChildColumnDefs(this.props);
}
同步网格属性更新 ( Synchronizing grid properties updates )
Once the grid is initialized, we need to track changes to React Props to update configuration options of the datagrid. ag-Grid implements API to do that, for example, if the headerHeight
property changes there's the setHeaderHeight
method to update the height of a header.
初始化网格后,我们需要跟踪对React Props的更改以更新数据网格的配置选项。 ag-Grid实现了API,例如,如果headerHeight
属性发生更改,则可以使用setHeaderHeight
方法来更新标头的高度。
React uses componentWillReceiveProps
lifecycle method to notify a component about changes. This is where we put our update logic:
React使用componentWillReceiveProps
生命周期方法来通知组件有关更改。 这是我们放置更新逻辑的地方:
export class AgGridReact extends React.Component {
componentWillReceiveProps(nextProps: any) {
const changes = <any>{};
const changedKeys = Object.keys(nextProps);
changedKeys.forEach((propKey) => {
...
if (!this.areEquivalent(this.props[propKey], nextProps[propKey])) {
changes[propKey] = {
previousValue: this.props[propKey],
currentValue: nextProps[propKey]
};
}
});
AgGrid.ComponentUtil.getEventCallbacks().forEach((funcName: string) => {
if (this.props[funcName] !== nextProps[funcName]) {
changes[funcName] = {
previousValue: this.props[funcName],
currentValue: nextProps[funcName]
};
}
});
AgGrid.ComponentUtil.processOnChange(changes, this.gridOptions, this.api, this.columnApi);
}
}
Basically we go over the list of ag-Grid's configuration properties and callbacks and check if any of them have changed. We put all changes in the changes
array and then process them using processOnChange
method.
基本上,我们浏览ag-Grid的配置属性和回调的列表,并检查它们是否已更改。 我们将所有更改放入changes
数组,然后使用processOnChange
方法对其进行处理。
The method does two things. First, it goes over the changes in React Props and updates properties on the gridOptions
object. Next, it calls API methods to notify the grid about the changes:
该方法有两件事。 首先,它将遍历React Props中的更改并更新gridOptions
对象上的属性。 接下来,它调用API方法以将更改通知网格:
export class ComponentUtil {
public static processOnChange(changes, gridOptions, api, ...) {
...
// reflect the changes in the gridOptions object
ComponentUtil.ARRAY_PROPERTIES
.concat(ComponentUtil.OBJECT_PROPERTIES)
.concat(ComponentUtil.STRING_PROPERTIES)
.forEach(key => {
if (changes[key]) {
gridOptions[key] = changes[key].currentValue;
}
});
...
// notify Grid about the changes in header height
if (changes.headerHeight) {
api.setHeaderHeight(changes.headerHeight.currentValue);
}
// notify Grid about the changes in page size
if (changes.paginationPageSize) {
api.paginationSetPageSize(changes.paginationPageSize.currentValue);
}
...
}
}
公开API ( Exposing API )
Interacting with the React grid at run time is done through the grid API. You may want to adjust the columns size, set new data source, get a list of all selected rows etc. When the JavaScript datagrid is initiated, it attaches the api
object to the grid options object. To expose this object, we simply assign it to the component instance:
通过网格API在运行时与React网格进行交互。 您可能需要调整列大小,设置新数据源,获取所有选定行的列表等。启动JavaScript datagrid时,它将api
对象附加到网格选项对象。 要公开此对象,我们只需将其分配给组件实例:
export class AgGridReact extends React.Component {
componentDidMount() {
...
new AgGrid.Grid(this.eGridDiv, this.gridOptions, gridParams);
this.api = this.gridOptions.api;
this.columnApi = this.gridOptions.columnApi;
}
}
And that's it.
就是这样。
翻译自: https://scotch.io/tutorials/wrap-a-vanilla-javascript-package-for-use-in-react
vanilla