向远程监视解决方案加速器 Web UI 添加自定义网格
本文介绍如何在远程监视解决方案加速器 Web UI 中的页面添加新网格。 本文介绍:
- 如何准备本地开发环境。
- 如何向 Web UI 中的页面添加新网格。
本文中的示例网格显示来自服务的数据,在向远程监视解决方案加速器 Web UI 添加自定义服务操作指南文章中介绍了服务的添加方法。
先决条件
要完成本操作指南中的步骤,需要在本地开发计算机上安装以下软件:
开始之前
应先完成下列文章中的步骤,再继续操作:
添加网格
要向 Web UI 添加网格,需要添加定义网格的源文件,然后修改部分现有文件,以使 Web UI 感知新组件。
添加定义网格的新文件
首先,src/walkthrough/components/pages/pageWithGrid/exampleGrid 文件夹包含定义网格的文件:
exampleGrid.js
import React, { Component } from 'react';
import { Btn, ComponentArray, PcsGrid } from 'components/shared';
import { exampleColumnDefs, defaultExampleGridProps } from './exampleGridConfig';
import { isFunc, svgs, translateColumnDefs } from 'utilities';
import { checkboxColumn } from 'components/shared/pcsGrid/pcsGridConfig';
const initialState = {
softSelectedId: undefined
};
/**
* A grid for displaying example data
*
* Encapsulates the PcsGrid props
*/
export class ExampleGrid extends Component {
constructor(props) {
super(props);
// Set the initial state
this.state = initialState;
// Default device grid columns
this.columnDefs = [
checkboxColumn,
exampleColumnDefs.id,
exampleColumnDefs.description
];
// Set up the available context buttons.
// If these are subject to user permissions, use the Protected component (src/components/shared/protected).
this.contextBtns =
<ComponentArray>
<Btn svg={svgs.reconfigure} onClick={this.clickContextBtn('btn1')}>{props.t('walkthrough.pageWithGrid.grid.btn1')}</Btn>
<Btn svg={svgs.trash} onClick={this.clickContextBtn('btn2')}>{props.t('walkthrough.pageWithGrid.grid.btn2')}</Btn>
</ComponentArray>;
}
/**
* Get the grid api options
*
* @param {Object} gridReadyEvent An object containing access to the grid APIs
*/
onGridReady = gridReadyEvent => {
this.gridApi = gridReadyEvent.api;
// Call the onReady props if it exists
if (isFunc(this.props.onGridReady)) {
this.props.onGridReady(gridReadyEvent);
}
};
clickContextBtn = (input) => () => {
//Just for demo purposes. Don't console log in a real grid.
console.log('Context button clicked', input);
console.log('Hard selected rows', this.gridApi.getSelectedRows());
};
/**
* Handles soft select props method.
* Soft selection happens when the user clicks on the row.
*
* @param rowId The id of the currently soft selected item
* @param rowData The rowData from the underlying grid. MAY BE OUT OF DATE.
*/
onSoftSelectChange = (rowId, rowData) => {
//Note: only the Id is reliable, rowData may be out of date
const { onSoftSelectChange } = this.props;
if (rowId) {
console.log('Soft selected', rowId); //Just for demo purposes. Don't console log a real grid.
this.setState({ softSelectedId: rowId });
}
if (isFunc(onSoftSelectChange)) {
onSoftSelectChange(rowId, rowData);
}
}
/**
* Handles context filter changes and calls any hard select props method.
* Hard selection happens when the user checks the box for the row.
*
* @param {Array} selectedObjs A list of currently selected objects in the grid
*/
onHardSelectChange = (selectedObjs) => {
const { onContextMenuChange, onHardSelectChange } = this.props;
// Show the context buttons when there are rows checked.
if (isFunc(onContextMenuChange)) {
onContextMenuChange(selectedObjs.length > 0 ? this.contextBtns : null);
}
if (isFunc(onHardSelectChange)) {
onHardSelectChange(selectedObjs);
}
}
getSoftSelectId = ({ id } = '') => id;
render() {
const gridProps = {
/* Grid Properties */
...defaultExampleGridProps,
columnDefs: translateColumnDefs(this.props.t, this.columnDefs),
sizeColumnsToFit: true,
getSoftSelectId: this.getSoftSelectId,
softSelectId: (this.state.softSelectedDevice || {}).id,
...this.props, // Allow default property overrides
deltaRowDataMode: true,
enableSorting: true,
unSortIcon: true,
getRowNodeId: ({ id }) => id,
context: {
t: this.props.t
},
/* Grid Events */
onRowClicked: ({ node }) => node.setSelected(!node.isSelected()),
onGridReady: this.onGridReady,
onSoftSelectChange: this.onSoftSelectChange,
onHardSelectChange: this.onHardSelectChange
};
return (
<PcsGrid key="example-grid-key" {...gridProps} />
);
}
}
exampleGridConfig.js
import Config from 'app.config';
import { SoftSelectLinkRenderer } from 'components/shared/cellRenderers';
/** A collection of column definitions for the example grid */
export const exampleColumnDefs = {
id: {
headerName: 'walkthrough.pageWithGrid.grid.name',
field: 'id',
sort: 'asc',
cellRendererFramework: SoftSelectLinkRenderer
},
description: {
headerName: 'walkthrough.pageWithGrid.grid.description',
field: 'descr'
}
};
/** Given an example object, extract and return the device Id */
export const getSoftSelectId = ({ id }) => id;
/** Shared example grid AgGrid properties */
export const defaultExampleGridProps = {
enableColResize: true,
multiSelect: true,
pagination: false,
paginationPageSize: Config.paginationPageSize,
rowSelection: 'multiple'
};
将 src/walkthrough/components/pages/pageWithGrid/exampleGrid 文件夹复制到 src/components/pages/example 文件夹。
向页面添加网格
按如下方式修改 src/components/pages/example/basicPage.container.js,以导入服务定义:
import { connect } from 'react-redux';
import { translate } from 'react-i18next';
import {
epics as exampleEpics,
getExamples,
getExamplesError,
getExamplesLastUpdated,
getExamplesPendingStatus
} from 'store/reducers/exampleReducer';
import { BasicPage } from './basicPage';
// Pass the data
const mapStateToProps = state => ({
data: getExamples(state),
error: getExamplesError(state),
isPending: getExamplesPendingStatus(state),
lastUpdated: getExamplesLastUpdated(state)
});
// Wrap the dispatch method
const mapDispatchToProps = dispatch => ({
fetchData: () => dispatch(exampleEpics.actions.fetchExamples())
});
export const BasicPageContainer = translate()(connect(mapStateToProps, mapDispatchToProps)(BasicPage));
按如下方式修改 src/components/pages/example/basicPage.js,以添加网格:
// Copyright (c) Microsoft. All rights reserved.
import React, { Component } from 'react';
import {
AjaxError,
ContextMenu,
PageContent,
RefreshBar
} from 'components/shared';
import { ExampleGrid } from './exampleGrid';
import './basicPage.css';
export class BasicPage extends Component {
constructor(props) {
super(props);
this.state = { contextBtns: null };
}
componentDidMount() {
const { isPending, lastUpdated, fetchData } = this.props;
if (!lastUpdated && !isPending) fetchData();
}
onGridReady = gridReadyEvent => this.gridApi = gridReadyEvent.api;
onContextMenuChange = contextBtns => this.setState({ contextBtns });
render() {
const { t, data, error, isPending, lastUpdated, fetchData } = this.props;
const gridProps = {
onGridReady: this.onGridReady,
rowData: isPending ? undefined : data || [],
onContextMenuChange: this.onContextMenuChange,
t: this.props.t
};
return [
<ContextMenu key="context-menu">
{this.state.contextBtns}
</ContextMenu>,
<PageContent className="basic-page-container" key="page-content">
<RefreshBar refresh={fetchData} time={lastUpdated} isPending={isPending} t={t} />
{!!error && <AjaxError t={t} error={error} />}
{!error && <ExampleGrid {...gridProps} />}
</PageContent>
];
}
}
按如下方式修改 src/components/pages/example/basicPage.test.js,以更新测试:
// Copyright (c) Microsoft. All rights reserved.
import React from 'react';
import { shallow } from 'enzyme';
import 'polyfills';
import { BasicPage } from './basicPage';
describe('BasicPage Component', () => {
it('Renders without crashing', () => {
const fakeProps = {
data: undefined,
error: undefined,
isPending: false,
lastUpdated: undefined,
fetchData: () => { },
t: () => { },
};
const wrapper = shallow(
<BasicPage {...fakeProps} />
);
});
});
测试网格
如果 Web UI 尚未在本地运行,请在存储库的本地副本的根目录中运行以下命令:
npm start
上述命令在本地 (https://localhost:3000/dashboard
) 运行 UI。 导航到“示例”页,查看显示服务中的数据的网格。
选择行
可通过两个选择,使用户选择网格中的行:
硬选择行
如果用户需要同时处理多行,对行使用复选框:
通过将 checkboxColumn 添加到向网格提供的 columnDefs,启用行的硬选择。 checkboxColumn 在 /src/components/shared/pcsGrid/pcsGrid.js 中定义:
this.columnDefs = [ checkboxColumn, exampleColumnDefs.id, exampleColumnDefs.description ];
要访问所选项,请获得对内部网格 API 的引用:
onGridReady = gridReadyEvent => { this.gridApi = gridReadyEvent.api; // Call the onReady props if it exists if (isFunc(this.props.onGridReady)) { this.props.onGridReady(gridReadyEvent); } };
硬选择网格中的行时,请提供页面的上下文按钮:
this.contextBtns = [ <Btn key="context-btn-1" svg={svgs.reconfigure} onClick={this.doSomething()}>Button 1</Btn>, <Btn key="context-btn-2" svg={svgs.trash} onClick={this.doSomethingElse()}>Button 2</Btn> ];
onHardSelectChange = (selectedObjs) => { const { onContextMenuChange, onHardSelectChange } = this.props; // Show the context buttons when there are rows checked. if (isFunc(onContextMenuChange)) { onContextMenuChange(selectedObjs.length > 0 ? this.contextBtns : null); } //... }
单击上下文按钮时,获取要在其上完成工作的硬选择项:
doSomething = () => { //Just for demo purposes. Don't console log in a real grid. console.log('Hard selected rows', this.gridApi.getSelectedRows()); };
软选择行
如果用户只需对单行操作,在 columnDefs 中为一列或多列配置软选择链接。
在 exampleGridConfig.js 中,添加 SoftSelectLinkRenderer 作为 columnDef 的 cellRendererFramework。
export const exampleColumnDefs = { id: { headerName: 'examples.grid.name', field: 'id', sort: 'asc', cellRendererFramework: SoftSelectLinkRenderer } };
单击软选择链接后,触发 onSoftSelectChange 事件。 对该行执行所需的任何操作,例如打开详细信息浮出控件。 此示例仅写入控制台:
onSoftSelectChange = (rowId, rowData) => { //Note: only the Id is reliable, rowData may be out of date const { onSoftSelectChange } = this.props; if (rowId) { //Just for demo purposes. Don't console log a real grid. console.log('Soft selected', rowId); this.setState({ softSelectedId: rowId }); } if (isFunc(onSoftSelectChange)) { onSoftSelectChange(rowId, rowData); } }
后续步骤
本文介绍了可以帮助你在远程监视解决方案加速器 Web UI 中添加或自定义页面的资源。
现在已定义了网格,下一步是向远程监视解决方案加速器 Web UI 添加自定义浮出控件,该浮出控件显示在示例页面上。
有关远程监视解决方案加速器的更多概念信息,请参阅 远程监视体系结构。