自定义场景对象中的变量
变量是交互式仪表板的基础。它们允许动态配置查询哪些数据。
除了标准的变量支持外,Scenes 还提供了一个 API,使自定义场景对象能够与变量一起工作。此 API 为仪表板创建者提供了更多可能性。
在自定义场景对象中使用变量
按照以下步骤使自定义场景对象对变量做出反应。
步骤 1. 构建自定义场景对象
首先构建一个自定义场景对象,该对象将显示提供的文本。
此对象将
- 包含一个简单的状态,其中包含一个字符串值(
text
属性)。 - 渲染一个
textarea
以修改状态,并显示一个预格式化的文本块以显示text
状态的当前值。
import { SceneObjectState, SceneObjectBase, SceneComponentProps } from '@grafana/scenes';
import { TextArea } from '@grafana/ui';
interface TextInterpolatorState extends SceneObjectState {
text: string;
}
class TextInterpolator extends SceneObjectBase<TextInterpolatorState> {
static Component = TextInterpolatorRenderer;
constructor(text: string) {
super({ text });
}
onTextChange = (text: string) => {
this.setState({ text });
};
}
function TextInterpolatorRenderer({ model }: SceneComponentProps<TextInterpolator>) {
const { text } = model.useState();
return (
<div>
<div style={{ marginBottom: 8 }}>
<TextArea defaultValue={text} onBlur={(e) => model.onTextChange(e.currentTarget.value)} />
</div>
<pre>{model.state.text}</pre>
</div>
);
}
步骤 2. 使用 TextInterpolator
构建场景
创建一个简单的场景,使用 TextInterpolator
const scene = new EmbeddedScene({
body: new SceneFlexLayout({
direction: 'column',
children: [
new SceneFlexItem({
minHeight: 300,
body: new TextInterpolator('Hello world'),
}),
],
}),
});
步骤 3. 向场景添加变量
定义一个自定义变量并将其添加到场景中
const greetingsVar = new CustomVariable({
name: 'greetings',
query: 'Hello , Hola , Bonjour , Ahoj',
});
const scene = new EmbeddedScene({
$variables: new SceneVariableSet({ variables: [greetingsVar] }),
controls: [new VariableValueSelectors({})],
body: new SceneFlexLayout({
direction: 'column',
children: [
new SceneFlexItem({
minHeight: 300,
body: new TextInterpolator('Hello world'),
}),
],
}),
});
步骤 4. 为 TextInterpolator
对象添加变量支持
使用 VariableDependencyConfig
使 TextInterpolator
对变量变化做出反应。在 TextInterpolator
中定义一个 protected _variableDependency
实例属性,它是一个 VariableDependencyConfig
的实例。
class TextInterpolator extends SceneObjectBase<TextInterpolatorState> {
static Component = TextInterpolatorRenderer;
protected _variableDependency = new VariableDependencyConfig(this, {
statePaths: ['text'],
});
constructor(text: string) {
super({ text });
}
onTextChange = (text: string) => {
this.setState({ text });
};
}
VariableDependencyConfig
接受具有以下配置选项的对象
statePaths
- 配置对象状态中可以包含变量的属性。使用['*']
指代对象状态中的任何属性。onReferencedVariableValueChanged
- 配置一个回调,当对象依赖的变量发生变化时执行。
如果为 VariableDependencyConfig
未指定 onReferencedVariableValueChanged
,则对象默认会在变量变化时重新渲染。
步骤 5. 在组件中插值 text
属性
在 TextInterpolatorRenderer
组件中,使用 sceneGraph.interpolate
辅助函数替换变量,当变量变化时替换 text
属性中的变量。
function TextInterpolatorRenderer({ model }: SceneComponentProps<TextInterpolator>) {
const { text } = model.useState();
const interpolatedText = sceneGraph.interpolate(model, text);
return (
<div>
<div style={{ marginBottom: 8 }}>
<TextArea defaultValue={text} onBlur={(e) => model.onTextChange(e.currentTarget.value)} />
</div>
<pre>{interpolatedText}</pre>
</div>
);
}
前面的代码将渲染一个具有模板变量、文本输入和预格式化文本块的场景。将文本输入中的文本修改为 ${greetings} World!
,预格式化文本框将更新。更改场景顶部的变量值,预格式化文本块也将更新。
自定义变量宏
您可以使用 sceneUtils.registerVariableMacro
注册自定义变量宏。变量宏对于您希望根据某些上下文动态评估的变量表达式非常有用。以下是一些作为宏实现的内置核心变量示例。
${__url.params:include:var-from,var-to}
${__user.login}
示例
export function getVariablesSceneWithCustomMacro() {
const scene = new EmbeddedScene({
// Attach the a behavior to the SceneApp or top level scene object that registers and unregisters the macro
$behaviors: [registerMacro],
controls: [new VariableValueSelectors({})],
body: new SceneFlexLayout({
direction: 'column',
children: [
new SceneFlexItem({
minHeight: 300,
body: new TextInterpolator('Testing my macro ${__sceneInfo.key}'),
}),
],
}),
});
return scene;
}
/**
* Macro to support ${__sceneInfo.<stateKey>} which will evaluate to the state key value of the
* context scene object where the string is interpolated.
*/
export class MyCoolMacro implements FormatVariable {
public state: { name: string; type: string };
public constructor(name: string, private _context: SceneObject) {
this.state = { name: name, type: '__sceneInfo' };
}
public getValue(fieldPath?: string) {
if (fieldPath) {
return (this._context.state as any)[fieldPath];
}
return this._context.state.key!;
}
public getValueText?(): string {
return '';
}
}
function registerMacro() {
const unregister = sceneUtils.registerVariableMacro('__sceneInfo', MyCoolMacro);
return () => unregister();
}
等待变量
当您有依赖于变量的状态逻辑时,可以使用 sceneGraph.hasVariableDependencyInLoadingState
检查是否所有变量依赖都已就绪(非加载状态)。如果任何依赖处于加载状态,这将返回 true,包括对完整依赖链的检查。
对于同时订阅时间和变量的对象,我们建议使用 VariableDependencyConfig
及其 onVariableUpdateCompleted
回调和 hasDependencyInLoadingState
函数。由于变量也可以根据时间做出反应并改变,为了避免双重反应,VariableDependencyConfig
具有内部状态以记住场景对象正在等待变量。为了利用这一点,请指定 onVariableUpdateCompleted
回调。此回调在依赖项值更改或场景对象等待变量时调用,或者在变量更新过程完成时调用。
示例设置
变量:A、B、C(B依赖于A,C依赖于B)。A依赖于时间范围,因此当时间范围改变时,它会加载新的值,这可能导致新的值(这会进一步导致B和C更新)。
带有依赖于变量C的查询的SceneQueryRunner
-
- 时间范围改变值
-
- 变量A开始加载
-
- SceneQueryRunner响应时间范围变化,尝试启动新查询,但在发出新查询之前调用
variableDependency.hasDependencyInLoadingState
。这检查变量C是否正在加载,它不是,然后检查变量B是否正在加载(因为它是C的依赖项),它也不是,然后检查A,A正在加载,因此返回true,SceneQueryRunner将跳过发出新查询。当这种情况发生时,VariableDependencyConfig将设置一个内部标志,表示它正在等待变量依赖项,这确保了在下一个变量完成onVariableUpdateCompleted时调用(无论完成的变量是直接依赖项还是其值已更改,我们只关心它已完成加载)。
- SceneQueryRunner响应时间范围变化,尝试启动新查询,但在发出新查询之前调用
-
- 变量A完成加载。选项(可能值)相同,因此没有更改值。
-
- SceneQueryRunner的VariableDependencyConfig收到变量A完成其加载阶段的通知,因为它处于等待变量状态,所以它将调用onVariableUpdateCompleted回调,即使A不是直接依赖项并且其值没有更改。