流程圖控件GoJS教程:模板圖
GoJS是一款功能強(qiáng)大,快速且輕量級(jí)的流程圖控件,可幫助你在JavaScript 和HTML5 Canvas程序中創(chuàng)建流程圖,且極大地簡(jiǎn)化您的JavaScript / Canvas 程序。
前面的許多示例都提供了用于節(jié)點(diǎn)、組或鏈接的自定義模板。這些示例說(shuō)明了如何通過(guò)數(shù)據(jù)綁定對(duì)特定數(shù)據(jù)實(shí)例的模板進(jìn)行簡(jiǎn)單調(diào)整。但是,如果您希望同時(shí)在單個(gè)圖中具有完全不同的外觀或行為的節(jié)點(diǎn)怎么辦?
可以定義一個(gè)節(jié)點(diǎn)模板,其中包括要顯示的所有類型節(jié)點(diǎn)的所有可能配置。要進(jìn)行所需的更改,將需要大量的數(shù)據(jù)綁定或代碼。通常,您將希望使GraphObject.visible模板的大部分不可見(jiàn),以便使要顯示的一個(gè)面板可見(jiàn)。但是這種技術(shù)很難使用-模板變得太復(fù)雜,太快。
相反,GoJS支持所需的任意數(shù)量的模板-您可以動(dòng)態(tài)選擇要用來(lái)表示特定節(jié)點(diǎn)數(shù)據(jù)的模板。這確實(shí)意味著潛在的大量模板,但是每個(gè)模板都將更加簡(jiǎn)單,易于編寫和維護(hù)。
每個(gè)圖實(shí)際上都為每種零件類型(節(jié)點(diǎn),組和鏈接)保留了一個(gè)模板映射。每個(gè)地圖都將“類別”名稱與模板相關(guān)聯(lián)。例如,當(dāng)圖想要為特定的節(jié)點(diǎn)數(shù)據(jù)對(duì)象創(chuàng)建一個(gè)節(jié)點(diǎn)時(shí),圖將使用該節(jié)點(diǎn)數(shù)據(jù)的類別在Diagram.nodeTemplateMap中查找節(jié)點(diǎn)模板。使用Diagram.groupTemplateMap和Diagram.linkTemplateMap可以完成類似的查找。
每個(gè)圖最初都有自己的模板圖,這些圖上存儲(chǔ)有預(yù)定義的類別。任何數(shù)據(jù)對(duì)象的默認(rèn)類別都是空字符串“”。該Diagram.nodeTemplateMap最初包含空字符串一個(gè)很簡(jiǎn)單的節(jié)點(diǎn)模板保存一個(gè)TextBlock中,其TextBlock.text屬性綁定到數(shù)據(jù)的數(shù)據(jù)轉(zhuǎn)換為字符串。您可以在許多前面的示例(例如“組和鏈接”示例)中看到節(jié)點(diǎn),組和鏈接的默認(rèn)模板。
Diagram.nodeTemplate的值就是thatDiagram.nodeTemplateMap.get(“”)的值。設(shè)置Diagram.nodeTemplate只是用空字符串替換了Diagram.nodeTemplateMap中命名的模板。
在Extensions目錄的Templates.js中提供了所有預(yù)定義模板的實(shí)現(xiàn)。創(chuàng)建自己的模板時(shí),您可能希望復(fù)制和修改這些定義。
節(jié)點(diǎn)模板示例
// 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" } ];
如果將鼠標(biāo)懸停在“Beta”節(jié)點(diǎn)上,您將看到顯示說(shuō)明字符串的工具提示。詳細(xì)的模板不會(huì)打擾使用工具提示來(lái)顯示其他信息,因?yàn)橐呀?jīng)顯示了所有內(nèi)容。
默認(rèn)情況下,模型和圖了解節(jié)點(diǎn)數(shù)據(jù)或鏈接數(shù)據(jù)的類別的方式是查看其類別屬性。如果要在數(shù)據(jù)上使用其他屬性,例如,由于要使用category屬性來(lái)表示不同的含義,請(qǐng)將Model.nodeCategoryProperty設(shè)置為產(chǎn)生實(shí)際類別字符串值的屬性的名稱?;?qū)odel.nodeCategoryProperty設(shè)置為空字符串,以使所有節(jié)點(diǎn)使用默認(rèn)節(jié)點(diǎn)模板。
項(xiàng)目模板示例
對(duì)于具有Panel.itemArray值的Panel,還有Panel.itemTemplateMap。與節(jié)點(diǎn),組和鏈接一樣,Panel.itemTemplate只是對(duì)在Panel.itemTemplateMap中以空字符串命名的模板的引用。同樣,Panel.itemCategoryProperty在項(xiàng)目數(shù)據(jù)上為屬性命名,該屬性用于標(biāo)識(shí)要從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" } ]);
表標(biāo)題顯示項(xiàng)目數(shù)據(jù)的示例
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: [ ] } );
為表面板具有不同標(biāo)題的自然方法是讓第一行(即第一項(xiàng))保存標(biāo)題的數(shù)據(jù),但是要采用不同的樣式。在此示例中,我們?cè)赑anel.itemTemplateMap中定義一個(gè)“標(biāo)題”項(xiàng)目模板。
如果您不想在itemArray中包含標(biāo)頭數(shù)據(jù),并且想要在節(jié)點(diǎn)模板中而不是在項(xiàng)目模板中定義標(biāo)頭,請(qǐng)參見(jiàn)Item Arrays中的示例。
更改零件的類別
要更改數(shù)據(jù)對(duì)象的表示形式,請(qǐng)調(diào)用Model.setCategoryForNodeData 或GraphLinksModel.setCategoryForLinkData。(如果設(shè)置了數(shù)據(jù)綁定的Part的Part.category,它將為您調(diào)用Model方法。)這將導(dǎo)致該圖丟棄該數(shù)據(jù)的任何現(xiàn)有Part并使用與該對(duì)象關(guān)聯(lián)的新模板重新創(chuàng)建新類別值。
// 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" } ];
單擊任何節(jié)點(diǎn)上的“星號(hào)”按鈕,可以在每個(gè)節(jié)點(diǎn)的“簡(jiǎn)單”類別和“詳細(xì)”類別之間動(dòng)態(tài)切換。
更改模板圖
您也可以替換一個(gè)或所有圖的模板映射(例如Diagram.nodeTemplateMap),以丟棄并重新創(chuàng)建圖中的所有節(jié)點(diǎn)。如果僅對(duì)節(jié)點(diǎn)使用默認(rèn)模板,則只需替換Diagram.nodeTemplate即可。
進(jìn)行此更改的一種常見(jiàn)情況是Diagram.scale更改。當(dāng)用戶縮小得足夠遠(yuǎn)時(shí),沒(méi)有必要對(duì)每個(gè)節(jié)點(diǎn)進(jìn)行過(guò)多的詳細(xì)說(shuō)明。
如果在此示例中縮小視圖,則DiagramEvent偵聽(tīng)器將檢測(cè)Diagram.scale何時(shí)足夠小以對(duì)所有節(jié)點(diǎn)使用更簡(jiǎn)單的模板。再次放大,然后突然使用更詳細(xì)的模板。
// 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,則不會(huì)通知該地圖已更改。您將需要顯式調(diào)用Diagram.rebuildParts。如果要替換Diagram.nodeTemplate或Diagram.nodeTemplateMap 或“組”或“鏈接”的相應(yīng)屬性,則Diagram屬性設(shè)置器將自動(dòng)調(diào)用Diagram.rebuildParts。
在圖中替換一個(gè)或多個(gè)模板時(shí),將自動(dòng)再次執(zhí)行布局。