本教程面向经验丰富的 JavaScript 开发者,并且对函数式编程技术有一定的了解。
在此示例中,我们将从条形图开始,这是一种原生 Looker 图表,其中会显示虚构的每周销售信息:
然后,我们将使用可视化组件来构建自定义可视化图表,显示每个品牌的产品在过去一个季度内的发展趋势。最终形成一种新的可视化效果,由一系列嵌套在表中的火花谱线图组成,如以下示例所示:
此示例不仅展示了如何创建自定义可视化图表,还演示了在 React 应用内使用 Looker API 的一些最佳做法。
如需使用 Looker 组件构建自定义的可视化图表,请确保您的设置符合要求,然后执行以下步骤:
如果自定义可视化图表适用于嵌入式应用或扩展程序,则适合使用可视化组件构建自定义可视化图表。如果您希望在整个 Looker 实例中向 Looker 用户提供自定义的可视化内容,请按照
visualization
文档页面上的说明进行操作。如果您要开发自定义的可视化图表并将其上传到 Looker Marketplace,请按照为 Looker Marketplace 开发自定义可视化图表文档页面中的说明操作。
使用要求
开始前,您需要准备以下几个元素:
- 您必须有权访问 Looker 实例。
- 无论您是在扩展框架中构建,还是在您自己的独立 React 应用中构建,都必须使用 Looker 的 API 进行身份验证并有权访问 Looker SDK 对象。如需了解详情,请参阅 Looker API 身份验证或我们的扩展程序框架。
- 确保您已安装 Looker 可视化组件 NPM 软件包和
@looker/components-data
NPM 软件包。如需了解如何安装和使用可视化组件包,请参阅自述文档(可在 GitHub 和 NPM 中找到)。
第 1 步:在“探索”中构建查询并复制查询 ID
在此示例中,我们针对假设一个季度我们会跟踪的品牌使用每周销售信息。
在“探索”中,我们可以使用 Looker 的原生可视化类型之一运行查询,并创建数据图表。该图表提供了大量信息,但很难一目了然地分析每个品牌的商品趋势:
与呈现简单可视化图表的示例一样,下一步是从“探索”的网址栏中复制 qid
值。在此示例中,qid
将为 4tQKzCBOwBGNWTskQyEpG8
。
第 2 步:将数据传递给自定义可视化组件
首先,将来自“探索”网址的 qid
值传递到 Query
组件,并将经过身份验证的 SDK 对象传递给 DataProvider
。
import React, { useContext } from 'react'
import { ExtensionContext } from '@looker/extension-sdk-react'
import { DataProvider } from '@looker/components-data'
import { Query } from '@looker/visualizations'
export const MyReactApp = () => {
const { core40SDK } = useContext(ExtensionContext)
return (
<DataProvider sdk={core40SDK}>
<Query query='4tQKzCBOwBGNWTskQyEpG8'></Query>
</DataProvider>
)
}
接下来,我们要构建一个名为 CustomVis
的自定义组件,而不是通过 Visualization
组件呈现原生 Looker 可视化图表。
Query
组件可以接受任何 React 元素作为子元素,并且仅会传递 config
、data
、fields
和 totals
值作为属性,以呈现您自己的可视化组件。我们会将 CustomVis
呈现为 Query
的子元素,因此它可以接收所有相关数据作为属性。
import React, { useContext } from 'react'
import { ExtensionContext } from '@looker/extension-sdk-react'
import { DataProvider } from '@looker/components-data'
import { Query } from '@looker/visualizations'
import { CustomVis } from '../path/to/MyCustomVis'
export const MyReactApp = () => {
const { core40SDK } = useContext(ExtensionContext)
return (
<DataProvider sdk={core40SDK}>
<Query query='4tQKzCBOwBGNWTskQyEpG8'>
<CustomVis />
</Query>
</DataProvider>
)
}
第 3 步:构建 CustomVis
组件
接下来,我们构建 CustomVis
组件。从 Query
组件继承的属性是 config
、fields
、data
和 totals
:
config
描述应在图表中呈现数据的所有方式,例如火花谱线图中的线条粗细或散点图的大小和形状。fields
会存储与查询返回的测量值和维度值有关的其他元数据,例如值的格式化方式或每条轴的标签。data
是从查询返回的键值对。totals
引用 Looker 的行总计值以用于基于表格的可视化。
我们可以通过插入 Table
组件,将这些未经修改的属性传递给表的可视化图表。
import React from 'react'
import { Table } from '@looker/visualizations'
export const CustomVis = ({ config, fields, data }) => {
return <Table config={config} data={data} fields={fields} />
}
这样,我们就可以了解直接从 SDK 返回的数据。在呈现的响应中,每个品牌每周都占据一行:
第 4 步:转换归一化数据
要从这些数据中获取时间序列趋势信息,我们需要按品牌名称对行进行分组。我们不希望每个品牌和每周都各占一行,而是希望每个品牌名称单独占据一行,并且嵌套在其中是一个日期和订单计数列表。
我们将创建一个自定义转换,将如下所示的值分组。以下是特定于这种情况的示例;您需要相应地解析自己的数据。
import React from 'react'
import { Table } from '@looker/visualizations'
import { filter, pick } from 'lodash'
const transformData = data => {
const brandKey = 'products.brand_name'
// create a unique set of brand names
const uniqueBrands = new Set(data.map(d => d[brandKey]))
// convert the Set back to an array and nest values below each brand name
// sample output:
// [{
// products.brand_name: "Looker",
// orders.count: { orders.created_week: '2019-09-30', orders.count: 17 }
// }, ...]
return Array.from(uniqueBrands).map(brand => {
const values = filter(data, { [brandKey]: brand }).map(d =>
pick(d, ['orders.created_week', 'orders.count'])
)
return { [brandKey]: brand, 'orders.count': values }
})
}
export const CustomVis = ({ config, fields, data }) => {
return <Table config={config} data={data} fields={fields} />
}
以下步骤会创建此函数:
- 首先,将引用
products.brand_name
分配给名为brandKey
的变量。这就是数据响应中的系列标签,也是对信息进行分组的方式。 - 接下来,通过在响应上映射并仅返回分配给
products.brand_name
的值,构建唯一的品牌名称列表。然后,这些对象会传入Set
构造函数。在 JavaScript 中,Set
是一个值出现一次的列表,会自动构建唯一值的列表。 - 然后,将唯一品牌的
Set
转换回数组,以使用列表转换实用程序,例如map
。 - 对于每个唯一品牌,请过滤原始数据响应,仅过滤出与该品牌名称相关的时序行。
- 最后,对于每个品牌名称,请将这些值嵌套在
orders.count
对象键下。在这里,您还可以看到一个示例,说明如何将品牌名称设置为字符串值,并将orders.count
设置为该品牌的所有相关值的列表。
第 5 步:将转换后的数据插入 CustomVis
现在,使用我们的新函数转换数据,并将输出分配给名为 nestedData
的新变量。
import React from 'react'
import { Table } from '@looker/visualizations'
import { filter, pick } from 'lodash'
const transformData = data => {
const brandKey = 'products.brand_name'
// create a unique set of brand names
const uniqueBrands = new Set(data.map(d => d[brandKey]))
// convert the Set back to an array and nest values below each brand name
// sample output:
// [{
// products.brand_name: "Looker",
// orders.count: { orders.created_week: '2019-09-30', orders.count: 17 }
// }, ...]
return Array.from(uniqueBrands).map(brand => {
const values = filter(data, { [brandKey]: brand }).map(d =>
pick(d, ['orders.created_week', 'orders.count'])
)
return { [brandKey]: brand, 'orders.count': values }
})
}
export const CustomVis = ({ config, fields, data }) => {
const nestedData = transformData(data)
return <Table config={config} data={data} fields={fields} />
}
接下来,遍历 nestedData
数组,并将嵌套值传递到由 Looker 的可视化组件库提供的标准火花谱线图中。
import React from 'react'
import { Table, Sparkline } from '@looker/visualizations'
import { filter, pick } from 'lodash'
const transformData = data => {
const brandKey = 'products.brand_name'
// create a unique set of brand names
const uniqueBrands = new Set(data.map(d => d[brandKey]))
// convert the Set back to an array and nest values below each brand name
// sample output:
// [{
// products.brand_name: "Looker",
// orders.count: { orders.created_week: '2019-09-30', orders.count: 17 }
// }, ...]
return Array.from(uniqueBrands).map(brand => {
const values = filter(data, { [brandKey]: brand }).map(d =>
pick(d, ['orders.created_week', 'orders.count'])
)
return { [brandKey]: brand, 'orders.count': values }
})
}
export const CustomVis = ({ config, fields, data }) => {
const nestedData = transformData(data)
const nestedSparklines = nestedData.map(d => {
return {
...d,
'orders.count': () => (
<Sparkline
data={d['orders.count']}
config={config}
fields={fields}
height={75}
/>
),
}
})
return <Table config={config} data={nestedSparklines} fields={fields} />
}
上述代码发生以下情况:
- 系统会再进行一次数据映射,为每一行创建一个新的火花谱线图。
- 嵌套在
orders.count
键下的值(属于相应品牌的日期系列和计数)会传递到Sparkline
的data
属性中。 - 系统还会传递所有图表所需的
config
和fields
对象。高度也设置为 75 像素,以便为表格中的每一行提供舒适的布局。 - 最后,嵌套图表对象会传入
Table
图表中。
第 6 步:生成自定义可视化图表
插入转换后的数据并配置图表后,图表将如下面的表格所示,每一行对应一个火花谱线图:
呈现上述可视化所需的完整代码如下:
import React, { useContext } from 'react'
import { ExtensionContext } from '@looker/extension-sdk-react'
import { DataProvider } from '@looker/components-data'
import { Query, Table, Sparkline } from '@looker/visualizations'
import { filter, pick } from 'lodash'
const transformData = data => {
const brandKey = 'products.brand_name'
// create a unique set of brand names
const uniqueBrands = new Set(data.map(d => d[brandKey]))
// convert the Set back to an array and nest values below each brand name
// sample output:
// [{
// products.brand_name: "Looker",
// orders.count: { orders.created_week: '2019-09-30', orders.count: 17 }
// }, ...]
return Array.from(uniqueBrands).map(brand => {
const values = filter(data, { [brandKey]: brand }).map(d =>
pick(d, ['orders.created_week', 'orders.count'])
)
return { [brandKey]: brand, 'orders.count': values }
})
}
export const CustomVis = ({ config, fields, data }) => {
const nestedData = transformData(data)
const nestedSparklines = nestedData.map(d => {
return {
...d,
'orders.count': () => (
<Sparkline
data={d['orders.count']}
config={config}
fields={fields}
height={75}
/>
),
}
})
return <Table config={config} data={nestedSparklines} fields={fields} />
}
export const MyReactApp = () => {
const { core40SDK } = useContext(ExtensionContext)
return (
<DataProvider sdk={core40SDK}>
<Query query='4tQKzCBOwBGNWTskQyEpG8'>
<CustomVis />
</Query>
</DataProvider>
)
}