处理交互式 Vega 传说中的事件 - 竞争条件

Handling events in interactive Vega legends - race condtion

linked chart仅包含一个图例,图例的作用如下:

图例显示由两个实体控制:

目前,如果只有一个水果名称打开,则单击它会将其关闭(因此所有水果名称都会关闭)。 我想修改此行为,以便单击最后启用的水果名称将打开所有内容。 (换句话说 - 不可能取消选择所有内容。)

为了打开一切,我只需要将信号 FILTERMODE 的值更改为 exclude。这是我遇到障碍的地方。 我在信号定义中尝试了以下内容:

"update": "event.shiftKey? 'include' : (length(data('selected'))? filtermode : 'exclude')",

这不起作用。我相当确定这是由于竞争条件而发生的。 当我检查数据的长度('source')时,它仍然是非空的。

所以事件的顺序如下:

最优雅的解决方法是什么?

您检查的是正确数组的长度吗?很难准确理解所需的行为是什么,但如果我添加代码(取决于过滤模式是包含还是排除)

length(data('selected')) == 6

length(data('selected')) == 0

然后它似乎工作。 Editor

试试这个。它与您的代码相同,但也会检查您的单行当前不执行的数组长度。

现在可以shift点击甜瓜,然后正常点击,滤镜模式切换。

Editor

{
  "$schema": "https://vega.github.io/schema/vega/v5.json",
  "description": "A scatter plot example with interactive legend and x-axis.",
  "width": 200,
  "height": 200,
  "padding": 5,
  "autosize": "pad",
  "signals": [
    {
      "name": "shift",
      "value": false,
      "on": [
        {
          "events": "@legendSymbol:click, @legendLabel:click",
          "update": "event.shiftKey",
          "force": true
        }
      ]
    },
    {
      "name": "clicked",
      "value": null,
      "on": [
        {
          "events": "@legendSymbol:click, @legendLabel:click",
          "update": "{value: datum.value}",
          "force": true
        }
      ]
    },
    {
      "name": "filtermode",
      "value": "exclude",
      "on": [
        {
          "events": "@legendSymbol:click, @legendLabel:click",
          "update": "event.shiftKey? 'include' : (length(data('selected') == 0)? filtermode : 'exclude')",
          "force": true
        }
      ]
    }
  ],
  "data": [
    {
      "name": "source",
      "values": [
        {"fruit": "apple"},
        {"fruit": "plum"},
        {"fruit": "pear"},
        {"fruit": "melon"},
        {"fruit": "grape"},
        {"fruit": "strawberry"}
      ]
    },
    {
      "name": "selected",
      "on": [
        {"trigger": "clicked && (event.shiftKey)", "remove": true},
        {"trigger": "clicked && (event.shiftKey)", "insert": "clicked"},
        {"trigger": "clicked && (!event.shiftKey)", "toggle": "clicked"}
      ]
    }
  ],
  "scales": [
    {
      "name": "color",
      "type": "ordinal",
      "range": {"scheme": "category10"},
      "domain": {"data": "source", "field": "fruit"}
    }
  ],
  "legends": [
    {
      "stroke": "color",
      "title": "Fruit",
      "encode": {
        "symbols": {
          "name": "legendSymbol",
          "interactive": true,
          "update": {
            "fill": {"value": "transparent"},
            "strokeWidth": {"value": 2},
            "opacity": [
              {
                "test": "filtermode == 'exclude' && !indata('selected', 'value', datum.value)",
                "value": 1
              },
              {
                "test": "filtermode == 'include' && indata('selected', 'value', datum.value)",
                "value": 1
              },
              {"value": 0.15}
            ],
            "size": {"value": 64}
          }
        },
        "labels": {
          "name": "legendLabel",
          "interactive": true,
          "update": {
            "opacity": [
              {
                "test": "filtermode == 'exclude' && !indata('selected', 'value', datum.value)",
                "value": 1
              },
              {
                "test": "filtermode == 'include' && indata('selected', 'value', datum.value)",
                "value": 1
              },
              {"value": 0.25}
            ]
          }
        }
      }
    }
  ]
}