跳到主要内容

自定义场景对象中的变量

变量为交互式仪表盘奠定了基础。它们允许对查询的数据进行动态配置。

除了标准的变量支持外,Scenes 还提供了一个 API,使自定义场景对象能够使用变量。这个 API 为仪表盘创建者提供了更多的可能性。

在自定义场景对象中使用变量

请按照以下步骤使自定义场景对象能够对变量作出反应。

步骤 1. 构建自定义场景对象

首先构建一个自定义场景对象,用于显示提供的文本。

此对象将

  1. 具有一个包含字符串值(text 属性)的简单状态。
  2. 渲染一个用于修改状态的 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 更新)。

SceneQueryRunner,其查询依赖于变量 C

    1. 时间范围更改值
    1. 变量 A 开始加载
    1. SceneQueryRunner 响应时间范围更改尝试启动新查询,但在发出新查询之前调用 variableDependency.hasDependencyInLoadingState。这会检查变量 C 是否正在加载,它不是,然后检查变量 B 是否正在加载(因为它是 C 的依赖项),它也不是,然后检查 A,A 正在加载,因此它返回 true,SceneQueryRunner 将跳过发出新查询。当发生这种情况时,VariableDependencyConfig 将设置一个内部标志,表示它正在等待变量依赖项,这确保了一旦下一个变量完成,就会调用 onVariableUpdateCompleted(无论完成的变量是否是直接依赖项,以及它的值是否已更改,我们只关心它完成了加载)。
    1. 变量 A 完成加载。选项(可能的值)相同,因此值没有变化。
    1. SceneQueryRunner 的 VariableDependencyConfig 收到变量 A 已完成其加载阶段的通知,因为它处于等待变量状态,即使 A 不是直接依赖项且其值未更改,它也会调用 onVariableUpdateCompleted 回调函数。

源代码

查看示例源代码