首页 > 解决方案 > Visjs使用画布外的按钮动态添加节点

问题描述

我正在尝试这样做 http://visjs.org/examples/network/data/dynamicData.html

但我无法使这些功能正常工作。我在角度 6。我希望能够在画布外部添加一个带有按钮的新节点,并且当我单击添加以在将其添加到画布之前有一个输入字段时

单击添加时出现此错误=无法读取未定义的属性“添加”

import { Component, OnInit } from '@angular/core';
import { Router, ActivatedRoute } from '@angular/router';
import { Network, DataSet, Node, Edge, IdType } from 'vis';
declare var vis

@Component({
  selector: 'app-micro-grid-management',
  templateUrl: './micro-grid-management.component.html',
  styleUrls: ['./micro-grid-management.component.css']
})
export class MicroGridManagement implements OnInit {

  constructor(private router: Router, private route: ActivatedRoute) { 
  
  }
  
  public nodes: Node;
  public edges: Edge;
  public network: Network;
  ngOnInit() {
    let nodes, edges, network
    nodes = new vis.DataSet();
    nodes.on('*', function () {
      document.getElementById('nodes').innerHTML = JSON
        .stringify(nodes.get(), null, 4);
    });
    nodes.add([
      {
        id: 1,
        label: 'PV Panels',
        currentP: '100 kW',
        setpp: '100 kW',
        currentq: '2 kVar',
        setq: '2 kVar',
        shape: 'square', color: '#ef6c00'
      },
      {
        id: 2,
        label: 'Wind Turbines',
        currentP: '100 kW',
        setpp: '100 kW',
        currentq: '2 kVar',
        setq: '2 kVar',
        shape: 'square', color: '#ef5350'
      },
      {
        id: 3,
        label: 'Gensets',
        currentP: '100 kW',
        setpp: '100 kW',
        currentq: '2 kVar',
        setq: '2 kVar',
        shape: 'square', color: '#00bfa5',
        font: { strokeWidth: 1, strokeColor: 'white' }
      },
      {
        id: 4,
        label: 'Battery',
        currentP: '100 kW',
        setpp: '100 kW',
        currentq: '2 kVar',
        setq: '2 kVar',
        shape: 'square', color: '#c2185b'
      },
      {
        id: 5,
        label: 'Fuelscells',
        currentP: '100 kW',
        setpp: '100 kW',
        currentq: '2 kVar',
        setq: '2 kVar',
        shape: 'square', color: '#5c6bc0'
      }
    ]);

    edges = new vis.DataSet();
    edges.on('*', function () {
      document.getElementById('edges').innerHTML = JSON
        .stringify(edges.get(), null, 4);
    });
    edges.add([
      { id: '1', from: '1', to: '2' },
      { id: '2', from: '1', to: '3' },
      { id: '3', from: '2', to: '4' },
      { id: '4', from: '2', to: '5' }
    ]);

    let container = document.getElementById('network');
    let data = {
      nodes: nodes,
      edges: edges
    };

    let options = {};
    network = new vis.Network(container, data, options);
  }
  
  toJSON(obj) {
       return JSON.stringify(obj, null, 4);
  }
  addNode() {
    try{
      this.nodes.add({
         id: document.getElementById('node-id'),
         label: document.getElementById('node-label')
      });
    }
    catch (err) {
      alert(err);
    }
  }

  updateNode() {
    id: document.getElementById('node-id');
    label: document.getElementById('node-label');
  }
}
<table>
    <tr>
        <td>
            <h2>Node</h2>
            <table>
                <tr>
                    <td></td>
                    <td><label for="node-id">Id</label></td>
                    <td><input id="node-id" type="text" value="6"></td>
                </tr>
                <tr>
                    <td></td>
                    <td><label for="node-label">Label</label></td>
                    <td><input id="node-label" type="text" value="Node 6"></td>
                </tr>
                <tr>
                    <td></td>
                    <td>Action</td>
                    <td>
                        <button id="node-add" onclick="addNode();">Add</button>
                        <button id="node-update" (click)="updateNode();">Update</button>
                        <button id="node-remove" (click)="removeNode();">Remove</button>
                    </td>
                </tr>
            </table>
        </td>
        <td>
            <h2>Edge</h2>
            <table>
                <tr>
                    <td></td>
                    <td><label for="edge-id">Id</label></td>
                    <td><input id="edge-id" type="text" value="5"></td>
                </tr>
                <tr>
                    <td></td>
                    <td><label for="edge-from">From</label></td>
                    <td><input id="edge-from" type="text" value="3"></td>
                </tr>
                <tr>
                    <td></td>
                    <td><label for="edge-to">To</label></td>
                    <td><input id="edge-to" type="text" value="4"></td>
                </tr>
                <tr>
                    <td></td>
                    <td>Action</td>
                    <td>
                        <button id="edge-add" (click)="addEdge();">Add</button>
                        <button id="edge-update" (click)="updateEdge();">Update</button>
                        <button id="edge-remove" (click)="removeEdge();">Remove</button>
                    </td>
                </tr>
            </table>
        </td>
    </tr>

</table>

<h1>View</h1>
<table class="view">
    <colgroup>
        <col width="25%">
        <col width="25%">
        <col width="50%">
    </colgroup>
    <tr>
        <td>
            <h2>Nodes</h2>
            <pre id="nodes"></pre>
        </td>

        <td>
            <h2>Edges</h2>
            <pre id="edges"></pre>
        </td>

        <td>
            <h2>Network</h2>

            <div id="network"></div>
        </td>
    </tr>
</table>

标签: javascriptangularvis.jsvis.js-network

解决方案


import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit } from '@angular/core';
import { Network, DataSet, Options } from 'vis';
import { BehaviorSubject } from 'rxjs';
import { RouterUIModel } from '../../../router/routers/router.model';
import { Circuit } from '../../../circuit/circuits/circuit.model';
import { RouterService } from '../../../router/routers/router.service';
import { CircuitService } from '../../../circuit/circuits/circuit.service';
import { MatDialog } from '@angular/material';
import { SaveGraphComponent } from '../save-graph/save-graph.component';
import { CircuitGraphModel, NetworkGraph, RouterGraphModel, NetworkModel, PositionJson } from '../network-graph.model';
import { GetSavedGraphComponent } from '../get-saved-graphs/get-saved-graphs.component';
import { NetworkGraphService } from '../network-graph.service';
import { TranslateService } from '@ngx-translate/core';
import { LayoutUtilsService } from '../../../../../../core/services/layout-utils.service';
import { AggregationDefinition } from '../../../circuit/aggregation-definition/aggregation-definition.model';
import { AggregationDefinitionService } from '../../../circuit/aggregation-definition/aggregation-definition.service';
import { AlertifyService } from '../../../../../../core/services/alertify.service';

@Component({
    selector: 'network-graph-list',
    templateUrl: './network-graph-list.component.html',
    styleUrls: ['./network-graph-list.component.scss'],
    changeDetection: ChangeDetectionStrategy.OnPush
})
export class NetworkGraphListComponent implements OnInit {
    network: Network = null;
    nodes: any = new DataSet();
    edges: any = new DataSet();

    fromRouterId: number = 0;
    fromRouterNode: RouterUIModel = new RouterUIModel();

    toRouterId: number = 0;
    toRouterNode: RouterUIModel = new RouterUIModel(); 

    routerNode: RouterUIModel = new RouterUIModel();

    routerSubject: BehaviorSubject<number> = new BehaviorSubject<number>(0); 
    changeSubject: BehaviorSubject<number> = new BehaviorSubject<number>(0);

    circuitToAggSubject: BehaviorSubject<number> = new BehaviorSubject<number>(0);
    aggToCircuitSubject: BehaviorSubject<number> = new BehaviorSubject<number>(0);

    circuitEdgeId: number = 0;
    circuitEdge: Circuit = new Circuit();

    aggDefId: number = 0;
    aggDef: AggregationDefinition;

    networkGraphId: number = 0;

    container: any;

    constructor(
        private cdr: ChangeDetectorRef,
        private routerService: RouterService,
        private circuitService: CircuitService,
        private dialog: MatDialog,
        private translate: TranslateService,
        private networkGraphService: NetworkGraphService,
        private layoutUtilsService: LayoutUtilsService,
        private aggDefService: AggregationDefinitionService,
        private alertifyService: AlertifyService
    ) {
    } 

    ngOnInit(): void {
        this.routerSubject.subscribe((routerSubjectId: number) => {
            this.routerService.getById(routerSubjectId).subscribe(res => {
                this.routerNode = res;
            });
        });

        this.draw();
        this.cdr.detectChanges();
    }

    draw(positions?: PositionJson[]) {
        try {
            this.container = document.getElementById("NetworkGraph")!;
            var options = {
                physics: {
                    enabled: false
                },
                interaction: {
                    hover: true
                }
            };
            this.network = new Network(this.container, {nodes: this.nodes,edges: this.edges}, options);

            if(positions != null){
                for(var position of positions){
                    this.network.moveNode(position.nodeId,position.nodeX,position.nodeY);
                }
            }

            this.network.setOptions({
            });

            // this.network.on("selectNode", function (params) {
            //  console.log("selectNode Event:", params);
            // });
            // this.network.on("selectEdge", function (params) {
            //  console.log("selectEdge Event:", params);
            // });
            // this.network.on("deselectNode", function (params) {
            //  console.log("deselectNode Event:", params);
            // });
            // this.network.on("deselectEdge", function (params) {
            //  console.log("deselectEdge Event:", params);
            // });

            this.network.on("hoverNode", (res) => {
                this.showTooltip(res);
            });

            this.network.on("blurNode", (res) => {
                this.removeTooltip(res);
           });
        }
        catch(err){
            console.log(err);
        }
          
        this.cdr.detectChanges();
    }
     
    showTooltip(res){
        this.container.appendChild(this.makeUlTooltip(["abc","def"]));
        this.container.innerHtml += this.makeUlTooltip(["abc","def"]); 
    }

    removeTooltip(res){
        document.getElementById("nodeTooltip").remove();
    }

    makeUlTooltip(array: string[]) {
        var list = document.createElement('ul');
        list.style.backgroundColor = "green";
        list.style.display = "block";
        list.style.position = "absolute";
        list.id = "nodeTooltip";
        list.style.zIndex = "100";
    
        for (var i = 0; i < array.length; i++) {
            var item = document.createElement('li');
            item.appendChild(document.createTextNode(array[i]));
            list.appendChild(item);
        }

        return list;
    }

    addNode() {
        try {
            if(this.routerNode === null){
                this.alertifyService.error("Please Select Router.");
                return;
            }
            else if(!Object.values(this.nodes._data as Object).some(o => o.id === this.routerNode.id)) {
                this.nodes.add({
                    id: this.routerNode.id,
                    label: this.routerNode.name,
                    shape: "circle",
                    color: "#3F51B5",
                    font: { size: 16, color: "white", face: "arial" },
                    margin: { top: 15, right: 15, bottom: 15, left: 15 },
                  });
    
                  this.nextRouterSubjectFunction();
                  this.nextChangeSubjectFunction();
            }
            else {
                this.alertifyService.error("Router is in the Graphic.");
                return;
            }

        } catch (err) {
            console.log(err);
        }

        this.cdr.detectChanges();
    }

    removeNode() {
        try {
            if(this.routerNode == null){
                this.alertifyService.error("Router Not Selected.");
                return;
            }
            if(!Object.values(this.nodes._data as Object).some(o => o.id === this.routerNode.id)){
                this.alertifyService.error("Router Not Found.");
                return;
            }
            else {
                this.nodes.remove({ id: this.routerNode.id });

                for(var releatedFromNodeEdge of Object.values(this.edges._data as Object).filter(o => o.from === this.routerNode.id)){
                    this.edges.remove({ id: releatedFromNodeEdge.id });
                }
                for(var releatedToNodeEdge of Object.values(this.edges._data as Object).filter(o => o.to === this.routerNode.id)){
                    this.edges.remove({ id: releatedToNodeEdge.id });
                }

                this.draw(this.preparePosition());
                this.nextRouterSubjectFunction();
                this.nextChangeSubjectFunction();
            }
        } catch (err) {
            console.log(err);
        }

        this.cdr.detectChanges();
    }

    addEdge() {
        try {
            if(Object.keys(this.circuitEdge).length === 0){
                this.alertifyService.error("Please Select Circuit.");
                return;
            }
            else if(!Object.values(this.edges._data as Object).some(o => o.id === this.circuitEdge.id)){
                this.edges.add({
                    id: this.circuitEdge.id,
                    from: this.fromRouterNode.id,
                    to: this.toRouterNode.id,
                    length: 400,
                    arrows: { from: { enabled: false, scaleFactor : 1}, to: { enabled: true, scaleFactor: 0.5 }},
                    label: this.circuitEdge.name+"\nInput Bps: "+this.circuitEdge.dataUtilization.inputBps+" bps"+"\nOutput Bps: "+this.circuitEdge.dataUtilization.outputBps+" bps",
                    color: { color: "red" },
                    type: this.circuitEdge.type
                  });
            }
            else {
                this.alertifyService.error("Circuit is in the Graphic.");
                return;
            }
        } catch (err) {
            console.log(err);
        }
        this.cdr.detectChanges();
    }
    
    removeEdge() {
        try {
            if(Object.keys(this.circuitEdge).length === 0){
                this.alertifyService.error("Please Select Circuit.");
                return;
            }
            if(!Object.values(this.edges._data as Object).some(o => o.id === this.circuitEdge.id)){
                this.alertifyService.error("Circuit Not Found.");
                return;
            }
            else {
                this.edges.remove({ id: this.circuitEdge.id });
                this.draw(this.preparePosition());
            }
        } catch (err) {
            console.log(err);
        }
        this.cdr.detectChanges();
    }
 
    saveGraph(){
        if(this.networkGraphId > 0){
            this.alertifyService.error("Network Graph Already Saved.");
            return;
        }
        var circuitGraphModels: CircuitGraphModel[] = [];
        for(var circuit of Object.values(this.edges._data as Object)){
            var circuitGraphModel: CircuitGraphModel = new CircuitGraphModel();
            circuitGraphModel.id = circuit.id;
            circuitGraphModel.label = circuit.label;
            circuitGraphModel.to = circuit.to;
            circuitGraphModel.from = circuit.from;
            circuitGraphModel.arrows = JSON.stringify(circuit.arrows);
            circuitGraphModel.type = circuit.type;
            circuitGraphModels.push(circuitGraphModel);
        }

        var routerGraphModels: RouterGraphModel[] = [];
        for(var router of Object.values(this.nodes._data as Object)){
            var routerGraphModel: RouterGraphModel = new RouterGraphModel();
            routerGraphModel.id = router.id;
            routerGraphModel.label = router.label;
            routerGraphModels.push(routerGraphModel);
        }

        var networkModel: NetworkModel = new NetworkModel();
        networkModel.clear();
        networkModel.circuitGraphModels = circuitGraphModels;
        networkModel.routerGraphModels = routerGraphModels;
        networkModel.positionJsons = this.preparePosition();

        var networkGraph: NetworkGraph = new NetworkGraph();
        networkGraph.clear();
        networkGraph.networkModel = networkModel;

        const dialogRef = this.dialog.open(SaveGraphComponent, { width: "500px", data: { networkGraph } });
        dialogRef.afterClosed().subscribe(res => {
            if(res === 0){
                return;
            }
            else if(res.Data === 0){
                return;
            } 
            else if(res.data > 0){
                this.networkGraphId = res.data;
            }
            else {
                return;
            }
        });
    }

    updateGraph(){
        if(this.networkGraphId === 0){
            this.alertifyService.error("Please Save Graph");
            return;
        }
        var circuitGraphModels: CircuitGraphModel[] = [];
        for(var circuit of Object.values(this.edges._data as Object)){
            var circuitGraphModel: CircuitGraphModel = new CircuitGraphModel();
            circuitGraphModel.id = circuit.id;
            circuitGraphModel.label = circuit.label;
            circuitGraphModel.to = circuit.to;
            circuitGraphModel.from = circuit.from;
            circuitGraphModel.arrows = JSON.stringify(circuit.arrows);
            circuitGraphModel.type = circuit.type;
            circuitGraphModels.push(circuitGraphModel);
            
        }

        var routerGraphModels: RouterGraphModel[] = [];
        for(var router of Object.values(this.nodes._data as Object)){
            var routerGraphModel: RouterGraphModel = new RouterGraphModel();
            routerGraphModel.id = router.id;
            routerGraphModel.label = router.label;
            routerGraphModels.push(routerGraphModel);
        }

        var networkModel: NetworkModel = new NetworkModel();
        networkModel.clear();
        networkModel.circuitGraphModels = circuitGraphModels;
        networkModel.routerGraphModels = routerGraphModels;
        networkModel.positionJsons = this.preparePosition();

        var networkGraph: NetworkGraph = new NetworkGraph();
        networkGraph.clear();
        networkGraph.id = this.networkGraphId;
        networkGraph.networkModel = networkModel;
        this.networkGraphService.getById(networkGraph.id).subscribe(ng => {
            networkGraph.name = ng.name;
            const dialogRef = this.dialog.open(SaveGraphComponent, { width: "500px", data: { networkGraph } });
            dialogRef.afterClosed().subscribe(res => {
                if(res === 0){
                    return;
                }
                else if(res.Data === 0){
                    return;
                } 
                else if(res.data > 0){
                    this.networkGraphId = res.data;
                }
                else {
                    return;
                }
            });
        });
    }

    getSavedGraphs(){
        const dialogRef = this.dialog.open(GetSavedGraphComponent, { width: "500px", data: {  } });
        dialogRef.afterClosed().subscribe(networkGraph => {
            if(networkGraph === 0){
                return;
            }
            else {
                this.networkGraphId = networkGraph.id;
                this.nextChangeSubjectFunction();
                this.nodes = new DataSet();
                for(var routerNode of networkGraph.networkModel.routerGraphModels){
                    this.nodes.add({
                        id: routerNode.id,
                        label: routerNode.label,
                        shape: "circle",
                        color: "#3F51B5",
                        font: { size: 16, color: "white", face: "arial" },
                        margin: { top: 15, right: 15, bottom: 15, left: 15 }
                      });
                }

                this.edges = new DataSet();
                for(var circuitEdge of networkGraph.networkModel.circuitGraphModels){
                    this.edges.add({
                        id: circuitEdge.id,
                        from: circuitEdge.from,
                        to: circuitEdge.to,
                        length: 400,
                        arrows: JSON.parse(circuitEdge.arrows),
                        label: circuitEdge.label,
                        color: { color: "red" },
                        type: circuitEdge.type
                    });
                }
            }
            this.draw(networkGraph.networkModel.positionJsons);
            this.nextChangeSubjectFunction();
        });
    }

    preparePosition(): PositionJson[] {
        var positionJsons: PositionJson[] = [];
        for(var position of Object.keys(this.network.getPositions() as Object)){
            var positionJson: PositionJson = new PositionJson();
            positionJson.nodeId = Number(position);
            positionJson.nodeX = this.network.getPositions()[position].x;
            positionJson.nodeY = this.network.getPositions()[position].y;
            positionJsons.push(positionJson);
        }
        return positionJsons;
    }

    deleteGraph(){
        const _title: string = this.translate.instant('COMMON.DELETE.TITLE');
        const _description: string = this.translate.instant('COMMON.DELETE.DESC');
        const _waitDescription: string = this.translate.instant('COMMON.DELETE.WAIT_DESC');

        const dialogRef = this.layoutUtilsService.deleteElement(_title, _description, _waitDescription);

        dialogRef.componentInstance.yesClick.subscribe((res) => {
            this.networkGraphService.delete(this.networkGraphId).subscribe(() => {
                if(this.networkGraphId > 0){
                    dialogRef.close();
                    this.networkGraphId = 0;
                    this.nodes = new DataSet();
                    this.edges = new DataSet();
                    this.draw();
                    this.nextChangeSubjectFunction();
                }
                else {
                    dialogRef.close();
                    this.alertifyService.error("Network Graph Not Saved.");
                }
            });
        });
    }

    refreshGraph(){
        this.networkGraphId = 0;
        this.nodes = new DataSet();
        this.edges = new DataSet();
        this.draw();
        this.nextChangeSubjectFunction();
    }

    nextRouterSubjectFunction(){
        this.routerSubject.next(0);
    }

    nextChangeSubjectFunction(){
        this.changeSubject.next(0);
    }

    changeCircuitEdge(){
        this.nextAggToCircuitSubjectFunction();
        this.circuitService.getCircuitById(this.circuitEdgeId).subscribe(res => {
            this.circuitEdge = res;
            this.circuitEdge.type = "Circuit";
        });
    }

    changeAggregationDefinition(){
        this.nextCircuitToAggSubjectFunction();
        this.aggDefService.getById(this.aggDefId).subscribe(res => {
            this.circuitEdge = res;
            this.circuitEdge.type = "Aggregation";
        });
    }

    changeFromRouter(){
        this.routerService.getById(this.fromRouterId).subscribe(res => {
            this.fromRouterNode = res;
        });
    }

    changeToRouter(){
        this.routerService.getById(this.toRouterId).subscribe(res => {
            this.toRouterNode = res;
        });
    }

    nextCircuitToAggSubjectFunction(){
        this.circuitToAggSubject.next(0);
    }

    nextAggToCircuitSubjectFunction(){
        this.aggToCircuitSubject.next(0);
    }
}
  
html,
body {
  font: 11pt arial;
}

h1 {
  font-size: 150%;
  margin: 5px 0;
}

h2 {
  font-size: 100%;
  margin: 5px 0;
}

table.view {
  width: 100%;
}

table td {
  vertical-align: top;
}

table table {
  background-color: #f5f5f5;
  border: 1px solid #e5e5e5;
}

table table td {
  vertical-align: middle;
}

input[type="text"],
pre {
  border: 1px solid lightgray;
}

pre {
  margin: 0;
  padding: 5px;
  font-size: 10pt;
}

#NetworkGraph {
  width: 100%;
  height: 700px;
}
<ng-container mPortletHeadTitle>
    <div class="m-portlet__head-title">
        <h4 class="m-portlet__head-text">
            <span translate="Network Graph"></span>
        </h4>
    </div>
</ng-container>
<br />
<div class="row">
    <div class="col-xl-2">
        <div class="mat-table__wrapper">
            <tbody>
                <tr>
                    <td>
                        <table>
                            <thead>
                                <tr>
                                    <th>
                                        <h6 class="m-portlet__head-text">
                                            <span translate="Routers"></span>
                                        </h6>
                                    </th>
                                </tr>
                            </thead>
                            <tbody>
                                <tr>
                                    <td>
                                        <router-subject-autocomplete
                                            [modelSubject]="routerSubject"
                                            [placeholder]="'Router'"
                                        ></router-subject-autocomplete>
                                    </td>
                                </tr>
                                <tr>
                                    <td>
                                        <button
                                            id="node-add"
                                            (click)="addNode()"
                                            mat-raised-button
                                            matTooltip="{{ 'Add' | translate }}"
                                            [style.background-color]="'#E60000'"
                                            [style.color]="'white'"
                                            type="button"
                                        >
                                            <span translate="Add"></span>
                                        </button>
                                        &nbsp;
                                        <button
                                            id="node-remove"
                                            (click)="removeNode()"
                                            mat-raised-button
                                            matTooltip="{{
                                                'Delete' | translate
                                            }}"
                                            [style.background-color]="'#E60000'"
                                            [style.color]="'white'"
                                            type="button"
                                        >
                                            <span translate="Delete"></span>
                                        </button>
                                    </td>
                                </tr>
                            </tbody>
                        </table>
                    </td>
                </tr>
                <br />
                <hr />
                <br />
                <tr>
                    <td>
                        <table>
                            <thead>
                                <tr>
                                    <th>
                                        <h6 class="m-portlet__head-text">
                                            <span translate="Circuits"></span>
                                        </h6>
                                    </th>
                                </tr>
                            </thead>
                            <tbody>
                                <tr>
                                    <td>
                                        <circuit-autocomplete
                                            [(model)]="circuitEdgeId"
                                            [circuitToAggSubject]="circuitToAggSubject"
                                            (modelChange)="changeCircuitEdge()"
                                        ></circuit-autocomplete>
                                    </td>
                                </tr>
                                <tr>
                                    <td>
                                        <agg-def-single-autocomplete
                                            [(model)]="aggDefId"
                                            [aggToCircuitSubject]="aggToCircuitSubject"
                                            (modelChange)="
                                                changeAggregationDefinition()
                                            "
                                        ></agg-def-single-autocomplete>
                                    </td>
                                </tr>
                                <tr>
                                    <td>
                                        <router-node-autocomplete
                                            [(model)]="fromRouterId"
                                            [changeSubject]="changeSubject"
                                            (modelChange)="changeFromRouter()"
                                            [(nodes)]="nodes"
                                            [placeholder]="'From Router'"
                                        ></router-node-autocomplete>
                                    </td>
                                </tr>
                                <tr>
                                    <td>
                                        <router-node-autocomplete
                                            [(model)]="toRouterId"
                                            [changeSubject]="changeSubject"
                                            (modelChange)="changeToRouter()"
                                            [(nodes)]="nodes"
                                            [placeholder]="'To Router'"
                                        ></router-node-autocomplete>
                                    </td>
                                </tr>
                                <tr>
                                    <td>
                                        <button
                                            id="edge-add"
                                            (click)="addEdge()"
                                            mat-raised-button
                                            matTooltip="{{ 'Add' | translate }}"
                                            [style.background-color]="'#E60000'"
                                            [style.color]="'white'"
                                            type="button"
                                        >
                                            <span translate="Add"></span>
                                        </button>
                                        &nbsp;
                                        <button
                                            id="edge-remove"
                                            (click)="removeEdge()"
                                            mat-raised-button
                                            matTooltip="{{
                                                'Delete' | translate
                                            }}"
                                            [style.background-color]="'#E60000'"
                                            [style.color]="'white'"
                                            type="button"
                                        >
                                            <span translate="Delete"></span>
                                        </button>
                                    </td>
                                </tr>
                            </tbody>
                        </table>
                    </td>
                </tr>
            </tbody>
        </div>
    </div>

    <div class="col-xl-10">
        <h1>Network</h1>
        <table class="view" style="background-color: white">
            <colgroup>
                <col width="50%" />
            </colgroup>
            <tbody>
                <tr>
                    <td>
                        <div style="padding: 15px">
                            <button
                                (click)="saveGraph()"
                                mat-icon-button
                                matTooltip="{{ 'Save Graph' | translate }}"
                                color="primary"
                                type="button"
                            >
                                <mat-icon>create</mat-icon></button
                            >&nbsp;
                            <button
                                (click)="updateGraph()"
                                mat-icon-button
                                matTooltip="{{ 'Update Graph' | translate }}"
                                color="primary"
                                type="button"
                            >
                                <mat-icon>update</mat-icon></button
                            >&nbsp;
                            <button
                                mat-icon-button
                                color="primary"
                                matTooltip="{{ 'saved graphs' | translate }}"
                                (click)="getSavedGraphs()"
                            >
                                <mat-icon>file_copy</mat-icon></button
                            >&nbsp;
                            <button
                                mat-icon-button
                                color="warn"
                                matTooltip="{{
                                    'COMMON.BUTTONS.DELETE' | translate
                                }}"
                                type="button"
                                (click)="deleteGraph()"
                            >
                                <mat-icon>delete</mat-icon>
                            </button>
                            &nbsp;
                            <button
                                mat-icon-button
                                color="primary"
                                matTooltip="{{
                                    'refresh' | translate
                                }}"
                                type="button"
                                (click)="refreshGraph()"
                            >
                                <mat-icon>sync</mat-icon>
                            </button>
                            &nbsp;
                        </div>
                        <div id="NetworkGraph"></div>
                    </td>
                </tr>
            </tbody>
        </table>
    </div>
</div>


推荐阅读