【问题标题】:Asynchronous update of nodes properties after the graph is rendered (Vivagraph.js)渲染图后节点属性的异步更新(Vivagraph.js)
【发布时间】:2015-08-01 20:02:38
【问题描述】:

我正在尝试在图表中异步填充节点的数据。

如何确保异步获取的数据实际绑定到图,并在准备好时呈现?

首先,您渲染图形结构、节点和链接。 其次,当数据准备好时,您将数据呈现为节点的属性。 新节点可以通过与父节点交互动态添加,不需要等待节点属性完成。

请注意我正在使用 Vivagraph.js 库。graph 是使用该库创建的对象,addLinks()getNode() 是它的函数属性 - 请参阅 Vivagraph demo 的演示,我将其用作我的尝试草稿。

我遇到的问题是节点一添加就在图中呈现 - addNode() 或者 addLinks(node1, node2) 函数 - 而异步获取节点的属性 - getNode(node).property = updatedValue - 结果 undefined

已编辑 - 基于注释的简化代码

下面我包含一个工作模型版本,基于这个(真棒)库的作者 @Anvaka 提供的教程。

我的目标是立即渲染图表,实现交互,并在从第三方获取数据时更新数据。

// attempt 1: fetch data async
var fetchInfo = function (graph, nodeId) {
    var root = 'http://jsonplaceholder.typicode.com';

    $.ajax({
        url: root + '/photos/' + nodeId,
        method: 'GET'
    }).then(function (data) {
        graph.getNode(nodeId).data = data.thumbnailUrl;
        console.log(graph.getNode(nodeId));
    });
};

// attempt 2: defer ajax directly
var fetchInfo_2 = function (graph, nodeId) {
    var root = 'http://jsonplaceholder.typicode.com';

    return $.ajax({
        url: root + '/photos/' + nodeId,
        method: 'GET'
    });
};

function main() {
    // As in previous steps, we create a basic structure of a graph:
    var graph = Viva.Graph.graph();

    graph.addLink(1, 2);
    fetchInfo(graph, 1); // updated data is undefined when graph is rendered 
    fetchInfo(graph, 2); // updated data is undefined when graph is rendered 

    /* trying a different outcome by deferring whole ajax
    graph.getNode(1).data = fetchInfo_2(1).done(function(data) {
      data.thumbnailUrl;
    }); // the whole object is deferred but cannot fetch data

    graph.getNode(2).data = fetchInfo_2(2).done(function(data) {
      data.thumbnailUrl;
    });  // the whole object is deferred but cannot fetch data
    */


    var graphics = Viva.Graph.View.svgGraphics(),
        nodeSize = 24,
        addRelatedNodes = function (nodeId, isOn) {
            for (var i = 0; i < 6; ++i) {
                var child = Math.floor((Math.random() * 150) + nodeId);
                // I add children and update data from external sources
                graph.addLink(nodeId, child);
                fetchInfo(graph, child);
            }
        };


    // dynamically add nodes on mouse interaction
    graphics.node(function (node) {
        var ui = Viva.Graph.svg('image')
            .attr('width', nodeSize)
            .attr('height', nodeSize)
            .link(node.data);

        console.log('rendered', node.id, node.data);

        $(ui).hover(function () {

            // nodes are rendered; nodes' data is undefined 
            addRelatedNodes(node.id);
        });
        return ui;
    }).placeNode(function (nodeUI, pos) {
        nodeUI.attr('x', pos.x - nodeSize / 2).attr('y', pos.y - nodeSize / 2);
    });

    graphics.link(function (link) {
        return Viva.Graph.svg('path')
            .attr('stroke', 'gray');
    }).placeLink(function (linkUI, fromPos, toPos) {
        var data = 'M' + fromPos.x + ',' + fromPos.y +
            'L' + toPos.x + ',' + toPos.y;

        linkUI.attr("d", data);
    })

    var renderer = Viva.Graph.View.renderer(graph, {
        graphics: graphics
    });
    renderer.run();
}

main();
svg {
    width: 100%;
    height: 100%;
}
<script src="https://rawgit.com/anvaka/VivaGraphJS/master/dist/vivagraph.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>

【问题讨论】:

  • 我很欣赏您尝试在您的问题中提供尽可能多的信息,但是您让读者不知所措,同时具有讽刺意味的是,您同时没有提供足够的信息。我们应该解决您的哪一项尝试?他们都是?不会发生。您的第一个代码示例中的第一行就是那里的语法错误。请至少提供语法正确的代码,即在复制和粘贴时实际运行的代码。 children 是什么? graph 是什么?你从来不说。请创建一个 (!) MCVE
  • 您好@Tomalak,感谢您提供 MCVE 指南。我要求从概念上指导如何异步更新使用 Vivagraph 库生成的图形的节点属性。异步获取的节点会添加到图中,但节点的属性不会更新。 Graph 是一个 vivagraph 对象(参见 url 中的演示); addNode() 和 getNode() 是 Viva 库中的函数。我的问题:节点上的属性异步更新导致渲染节点时未定义。我尝试的目的是解释我尝试做的事情,而不是修复代码。我的问题更清楚了吗?
  • 嗯,从概念上来说你在该进程的回调中完成所有来自异步进程的工作。这包括任何渲染/重新渲染工作。正如我所说,我了解您要对许多代码示例做什么。 A)我是说它没有帮助,相反,它会适得其反。 B) 我已要求您创建一个 (!) 代码当前状态的独立示例。
  • @Tomalak 感谢您的反馈,我按照您的建议做了一个简单的工作简化示例 - 希望它现在更清晰和有用!

标签: asynchronous graph callback promise vivagraphjs


【解决方案1】:

正如我在 cmets 中所说,由于异步过程而要执行的任何工作都必须在回调中完成。这包括任何渲染工作。

对于 jQuery 的延迟(如 Ajax 调用的结果),回调是通过 .then.done 定义的,使您能够将获取资源的行为与使用该资源的操作分离。

设置图像源是渲染工作,所以必须在回调中进行。下面是一个获取图像并返回延迟结果的函数,node 回调函数使用它来完成自己的工作。

function fetchInfo(id) {
    var root = 'http://jsonplaceholder.typicode.com';
    return $.getJSON(root + '/photos/' + id);
}

function main() {
    var graph = Viva.Graph.graph(),
        graphics = Viva.Graph.View.svgGraphics(),
        nodeSize = 24,
        addRelatedNodes = function (nodeId, count) {
            var childId, i;
            for (i = 0; i < count; ++i) {
                childId = Math.floor(Math.random() * 150) + nodeId;
                graph.addLink(nodeId, childId);
            }
        };
    
    graphics
        .node(function (node) {
            var ui = Viva.Graph.svg('image')
                .attr('width', nodeSize)
                .attr('height', nodeSize)
                .link('http://www.ajaxload.info/images/exemples/24.gif');
            
            $(ui).dblclick(function () {
                addRelatedNodes(node.id, 6);
            });

            // render when ready
            fetchInfo(node.id).done(function (data) {
                ui.link(data.thumbnailUrl);
            });

            return ui;
        })
        .placeNode(function (nodeUI, pos) {
            nodeUI.attr('x', pos.x - nodeSize / 2).attr('y', pos.y - nodeSize / 2);
        })
        .link(function (link) {
            return Viva.Graph.svg('path')
                .attr('stroke', 'gray');
        })
        .placeLink(function (linkUI, fromPos, toPos) {
            var data = 
                'M' + fromPos.x + ',' + fromPos.y +
                'L' + toPos.x + ',' + toPos.y;

            linkUI.attr("d", data);
        });

    graph.addLink(1, 2);

    Viva.Graph.View.renderer(graph, {
        graphics: graphics
    }).run();
}

main();
svg {
    width: 100%;
    height: 100%;
}
img {
    cursor: pointer;
}
<script src="https://rawgit.com/anvaka/VivaGraphJS/master/dist/vivagraph.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>

遗憾的是,它似乎没有在 StackSnippets 中运行,但它可以在 jsFiddle 上运行:http://jsfiddle.net/tL9992ua/

【讨论】:

  • 感谢@Tomalak 的解决方案以及如何编辑的建议。我需要另一个简报。我想避免重复查询 each 节点。所以我获取了第一组具有“name”属性的 N 个孩子。问题是,当节点作为 graph.addLink() 的结果放置在图上时,“名称”是未定义的原因。 (在您的示例中,如果我为每个新节点获取数据 ,if 将起作用)。是否可以多次推迟和重用一个对象?使用哈希表存储所有信息会是一个很好的实现吗?如何仅在更新时使用属性?
  • 基本上我现在要做的就是这样。我有一个具有 name 属性的对象,它在从外部源获取值时存储值。该对象已被渲染。我想向将要呈现的变量承诺属性的值。有可能吗?我正在尝试类似:name = new Promise(function(id) { return ht.getItem(id).name //get property 'name' from an item in object HashTable }); 但我得到:[[PromiseStatus]]: "rejected", [[PromiseValue]]: TypeError: Cannot read property 'name' of undefined
  • 当然,您可以随时存储和重复使用延迟。如果您想批量获取,只需执行此操作并缓存生成的延迟。现在您可以根据需要随时对它们调用.done(),例如每个节点一次。只要 Ajax 响应处于挂起状态,.done() 回调就会等待,响应到达后,它们会立即运行。
  • 除非您的页面中有专门的 promise 库,否则出于兼容性考虑,我建议您不要使用 new Promise。而且你也不需要使用它,你可以简单地存储 jQuery Ajax 请求。
  • @user305883 对比jsfiddle.net/tL9992ua/1,这里我只修改了fetchInfo()函数,其余代码与初始版本完全相同。请注意,我已经切换到 jQuery 1.8+,其中引入了这种通过.then() 链接的承诺。我认为您不会坚持使用像 1.7.2 一样古老的 jQuery 版本。
猜你喜欢
  • 1970-01-01
  • 2019-06-18
  • 2019-04-21
  • 2020-07-18
  • 1970-01-01
  • 2019-02-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多