流程圖控件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。
在圖中替換一個或多個模板時,將自動再次執行布局。