流程图控件GoJS教程:模板图
GoJS是一款功能强大,快速且轻量级的流程图控件,可帮助你在JavaScript 和HTML5 Canvas程序中创建流程图,且极大地简化您的JavaScript / Canvas 程序。
前面的许多示例都提供了用于节点、组或链接的自定义模板。这些示例说明了如何通过数据绑定对特定数据实例的模板进行简单调整。但是,如果您希望同时在单个图中具有完全不同的外观或行为的节点怎么办?
可以定义一个节点模板,其中包括要显示的所有类型节点的所有可能配置。要进行所需的更改,将需要大量的数据绑定或代码。通常,您将希望使GraphObject.visible模板的大部分不可见,以便使要显示的一个面板可见。但是这种技术很难使用-模板变得太复杂,太快。
相反,GoJS支持所需的任意数量的模板-您可以动态选择要用来表示特定节点数据的模板。这确实意味着潜在的大量模板,但是每个模板都将更加简单,易于编写和维护。
每个图实际上都为每种零件类型(节点,组和链接)保留了一个模板映射。每个地图都将“类别”名称与模板相关联。例如,当图想要为特定的节点数据对象创建一个节点时,图将使用该节点数据的类别在Diagram.nodeTemplateMap中查找节点模板。使用Diagram.groupTemplateMap和Diagram.linkTemplateMap可以完成类似的查找。
每个图最初都有自己的模板图,这些图上存储有预定义的类别。任何数据对象的默认类别都是空字符串“”。该Diagram.nodeTemplateMap最初包含空字符串一个很简单的节点模板保存一个TextBlock中,其TextBlock.text属性绑定到数据的数据转换为字符串。您可以在许多前面的示例(例如“组和链接”示例)中看到节点,组和链接的默认模板。
Diagram.nodeTemplate的值就是thatDiagram.nodeTemplateMap.get(“”)的值。设置Diagram.nodeTemplate只是用空字符串替换了Diagram.nodeTemplateMap中命名的模板。
在Extensions目录的Templates.js中提供了所有预定义模板的实现。创建自己的模板时,您可能希望复制和修改这些定义。
节点模板示例
// the "simple" template just shows the key string and the color in the background, // but it also includes a tooltip that shows the description var simpletemplate = $(go.Node, "Auto", $(go.Shape, "Ellipse", new go.Binding("fill", "color")), $(go.TextBlock, new go.Binding("text", "key")), { toolTip: $("ToolTip", $(go.TextBlock, { margin: 4 }, new go.Binding("text", "desc")) ) } ); // the "detailed" template shows all of the information in a Table Panel var detailtemplate = $(go.Node, "Auto", $(go.Shape, "RoundedRectangle", new go.Binding("fill", "color")), $(go.Panel, "Table", { defaultAlignment: go.Spot.Left }, $(go.TextBlock, { row: 0, column: 0, columnSpan: 2, font: "bold 12pt sans-serif" }, new go.Binding("text", "key")), $(go.TextBlock, { row: 1, column: 0 }, "Description:"), $(go.TextBlock, { row: 1, column: 1 }, new go.Binding("text", "desc")), $(go.TextBlock, { row: 2, column: 0 }, "Color:"), $(go.TextBlock, { row: 2, column: 1 }, new go.Binding("text", "color")) ) ); // create the nodeTemplateMap, holding three node templates: var templmap = new go.Map(); // In TypeScript you could write: new go.Map<string, go.Node>(); // for each of the node categories, specify which template to use templmap.add("simple", simpletemplate); templmap.add("detailed", detailtemplate); // for the default category, "", use the same template that Diagrams use by default; // this just shows the key value as a simple TextBlock templmap.add("", diagram.nodeTemplate); diagram.nodeTemplateMap = templmap; diagram.model.nodeDataArray = [ { key: "Alpha", desc: "first letter", color: "green" }, // uses default category: "" { key: "Beta", desc: "second letter", color: "lightblue", category: "simple" }, { key: "Gamma", desc: "third letter", color: "pink", category: "detailed" }, { key: "Delta", desc: "fourth letter", color: "cyan", category: "detailed" } ];
如果将鼠标悬停在“Beta”节点上,您将看到显示说明字符串的工具提示。详细的模板不会打扰使用工具提示来显示其他信息,因为已经显示了所有内容。
默认情况下,模型和图了解节点数据或链接数据的类别的方式是查看其类别属性。如果要在数据上使用其他属性,例如,由于要使用category属性来表示不同的含义,请将Model.nodeCategoryProperty设置为产生实际类别字符串值的属性的名称。或将Model.nodeCategoryProperty设置为空字符串,以使所有节点使用默认节点模板。
项目模板示例
对于具有Panel.itemArray值的Panel,还有Panel.itemTemplateMap。与节点,组和链接一样,Panel.itemTemplate只是对在Panel.itemTemplateMap中以空字符串命名的模板的引用。同样,Panel.itemCategoryProperty在项目数据上为属性命名,该属性用于标识要从itemTemplateMap使用的模板。
// create a template map for items var itemtemplates = new go.Map(); // In TypeScript you could write: new go.Map<string, go.Panel>(); // the template when type == "text" itemtemplates.add("text", $(go.Panel, $(go.TextBlock, new go.Binding("text")) )); // the template when type == "button" itemtemplates.add("button", $("Button", $(go.TextBlock, new go.Binding("text")), // convert a function name into a function value, // because functions cannot be represented in JSON format new go.Binding("click", "handler", function(name) { if (name === "alert") return raiseAlert; // defined below return null; }) )); diagram.nodeTemplate = $(go.Node, "Vertical", $(go.TextBlock, new go.Binding("text", "key")), $(go.Panel, "Auto", $(go.Shape, { fill: "white" }), $(go.Panel, "Vertical", { margin: 3, defaultAlignment: go.Spot.Left, itemCategoryProperty: "type", // this property controls the template used itemTemplateMap: itemtemplates // map was defined above }, new go.Binding("itemArray", "info")) ) ); function raiseAlert(e, obj) { // here OBJ will be the item Panel var node = obj.part; alert(node.data.key + ": " + obj.data.text); } // The model data includes item arrays in the node data. diagram.model = new go.GraphLinksModel( [ { key: "Alpha", info: [ { type: "text", text: "some text" }, { type: "button", text: "Click me!", handler: "alert"} ] }, { key: "Beta", info: [ { type: "text", text: "first line" }, { type: "button", text: "First Button", handler: "alert"}, { type: "text", text: "second line" }, { type: "button", text: "Second Button", handler: "alert" } ] } ],[ { from: "Alpha", to: "Beta" } ]);
表标题显示项目数据的示例
var itemTemplateMap = new go.Map(); itemTemplateMap.add("", $(go.Panel, "TableRow", $(go.TextBlock, new go.Binding("text", "name"), { column: 0, margin: 2, font: "bold 10pt sans-serif" }), $(go.TextBlock, new go.Binding("text", "phone"), { column: 1, margin: 2 }), $(go.TextBlock, new go.Binding("text", "loc"), { column: 2, margin: 2 }) )); itemTemplateMap.add("Header", $(go.Panel, "TableRow", $(go.TextBlock, new go.Binding("text", "name"), { column: 0, margin: 2, font: "bold 10pt sans-serif" }), $(go.TextBlock, new go.Binding("text", "phone"), { column: 1, margin: 2, font: "bold 10pt sans-serif" }), $(go.TextBlock, new go.Binding("text", "loc"), { column: 2, margin: 2, font: "bold 10pt sans-serif" }) )); diagram.nodeTemplate = $(go.Node, "Auto", $(go.Shape, { fill: "white" }), $(go.Panel, "Table", new go.Binding("itemArray", "people"), { defaultAlignment: go.Spot.Left, defaultColumnSeparatorStroke: "black", itemTemplateMap: itemTemplateMap }, $(go.RowColumnDefinition, { row: 0, background: "lightgray" }), $(go.RowColumnDefinition, { row: 1, separatorStroke: "black" }) ) ); diagram.model = $(go.GraphLinksModel, { nodeDataArray: [ { key: "group1", people: [ { name: "Person", phone: "Phone", loc: "Location", category: "Header" }, { name: "Alice", phone: "2345", loc: "C4-E18" }, { name: "Bob", phone: "9876", loc: "E1-B34" }, { name: "Carol", phone: "1111", loc: "C4-E23" }, { name: "Ted", phone: "2222", loc: "C4-E197" }, { name: "Robert", phone: "5656", loc: "B1-A27" }, { name: "Natalie", phone: "5698", loc: "B1-B6" } ] } ], linkDataArray: [ ] } );
为表面板具有不同标题的自然方法是让第一行(即第一项)保存标题的数据,但是要采用不同的样式。在此示例中,我们在Panel.itemTemplateMap中定义一个“标题”项目模板。
如果您不想在itemArray中包含标头数据,并且想要在节点模板中而不是在项目模板中定义标头,请参见Item Arrays中的示例。
更改零件的类别
要更改数据对象的表示形式,请调用Model.setCategoryForNodeData 或GraphLinksModel.setCategoryForLinkData。(如果设置了数据绑定的Part的Part.category,它将为您调用Model方法。)这将导致该图丢弃该数据的任何现有Part并使用与该对象关联的新模板重新创建新类别值。
// this function changes the category of the node data to cause the Node to be replaced function changeCategory(e, obj) { var node = obj.part; if (node) { var diagram = node.diagram; diagram.startTransaction("changeCategory"); var cat = diagram.model.getCategoryForNodeData(node.data); if (cat === "simple") cat = "detailed"; else cat = "simple"; diagram.model.setCategoryForNodeData(node.data, cat); diagram.commitTransaction("changeCategory"); } } // The "simple" template just shows the key string and the color in the background. // There is a Button to invoke the changeCategory function. var simpletemplate = $(go.Node, "Spot", $(go.Panel, "Auto", $(go.Shape, "Ellipse", new go.Binding("fill", "color")), $(go.TextBlock, new go.Binding("text", "key")) ), $("Button", { alignment: go.Spot.TopRight }, $(go.Shape, "AsteriskLine", { width: 8, height: 8 }), { click: changeCategory }) ); // The "detailed" template shows all of the information in a Table Panel. // There is a Button to invoke the changeCategory function. var detailtemplate = $(go.Node, "Spot", $(go.Panel, "Auto", $(go.Shape, "RoundedRectangle", new go.Binding("fill", "color")), $(go.Panel, "Table", { defaultAlignment: go.Spot.Left }, $(go.TextBlock, { row: 0, column: 0, columnSpan: 2, font: "bold 12pt sans-serif" }, new go.Binding("text", "key")), $(go.TextBlock, { row: 1, column: 0 }, "Description:"), $(go.TextBlock, { row: 1, column: 1 }, new go.Binding("text", "desc")), $(go.TextBlock, { row: 2, column: 0 }, "Color:"), $(go.TextBlock, { row: 2, column: 1 }, new go.Binding("text", "color")) ) ), $("Button", { alignment: go.Spot.TopRight }, $(go.Shape, "AsteriskLine", { width: 8, height: 8 }), { click: changeCategory }) ); var templmap = new go.Map(); // In TypeScript you could write: new go.Map<string, go.Node>(); templmap.add("simple", simpletemplate); templmap.add("detailed", detailtemplate); diagram.nodeTemplateMap = templmap; diagram.layout = $(go.TreeLayout); diagram.model.nodeDataArray = [ { key: "Beta", desc: "second letter", color: "lightblue", category: "simple" }, { key: "Gamma", desc: "third letter", color: "pink", category: "detailed" }, { key: "Delta", desc: "fourth letter", color: "cyan", category: "detailed" } ]; diagram.model.linkDataArray = [ { from: "Beta", to: "Gamma" }, { from: "Gamma", to: "Delta" } ];
单击任何节点上的“星号”按钮,可以在每个节点的“简单”类别和“详细”类别之间动态切换。
更改模板图
您也可以替换一个或所有图的模板映射(例如Diagram.nodeTemplateMap),以丢弃并重新创建图中的所有节点。如果仅对节点使用默认模板,则只需替换Diagram.nodeTemplate即可。
进行此更改的一种常见情况是Diagram.scale更改。当用户缩小得足够远时,没有必要对每个节点进行过多的详细说明。
如果在此示例中缩小视图,则DiagramEvent侦听器将检测Diagram.scale何时足够小以对所有节点使用更简单的模板。再次放大,然后突然使用更详细的模板。
// The "simple" template just shows the key string and the color in the background. var simpletemplate = $(go.Node, "Spot", $(go.Panel, "Auto", $(go.Shape, "Ellipse", new go.Binding("fill", "color")), $(go.TextBlock, new go.Binding("text", "key")) ) ); // The "detailed" template shows all of the information in a Table Panel. var detailtemplate = $(go.Node, "Spot", $(go.Panel, "Auto", $(go.Shape, "RoundedRectangle", new go.Binding("fill", "color")), $(go.Panel, "Table", { defaultAlignment: go.Spot.Left }, $(go.TextBlock, { row: 0, column: 0, columnSpan: 2, font: "bold 12pt sans-serif" }, new go.Binding("text", "key")), $(go.TextBlock, { row: 1, column: 0 }, "Description:"), $(go.TextBlock, { row: 1, column: 1 }, new go.Binding("text", "desc")), $(go.TextBlock, { row: 2, column: 0 }, "Color:"), $(go.TextBlock, { row: 2, column: 1 }, new go.Binding("text", "color")) ) ) ); diagram.layout = $(go.TreeLayout); diagram.model.nodeDataArray = [ { key: "Beta", desc: "second letter", color: "lightblue" }, { key: "Gamma", desc: "third letter", color: "pink" }, { key: "Delta", desc: "fourth letter", color: "cyan" } ]; diagram.model.linkDataArray = [ { from: "Beta", to: "Gamma" }, { from: "Gamma", to: "Delta" } ]; // initially use the detailed templates diagram.nodeTemplate = detailtemplate; diagram.addDiagramListener("ViewportBoundsChanged", function (e) { if (diagram.scale < 0.9) { diagram.nodeTemplate = simpletemplate; } else { diagram.nodeTemplate = detailtemplate; } }); myDiagram = diagram; // make accessible to the HTML buttons
警告:如果您修改模板Map,则不会通知该地图已更改。您将需要显式调用Diagram.rebuildParts。如果要替换Diagram.nodeTemplate或Diagram.nodeTemplateMap 或“组”或“链接”的相应属性,则Diagram属性设置器将自动调用Diagram.rebuildParts。
在图中替换一个或多个模板时,将自动再次执行布局。