场景
Panel
的 itemArray
属性,配合 itemTemplate:go.Panel
。可以实现将数组内每个元素都映射到图表上。
最初的实现逻辑大致如下。
下面是节点定义:
GO(go.Node, "Spot",
{itemTemplate: nodePortTemplate},
new go.Binding("itemArray", "portArray"),
makeNodeTemplate(type, stretch, size),
}
其中 makeNodeTemplate
就是返回了 go.Picture|go.Shape
对象。
nodePortTemplate
如下:
GO(go.Panel, "Spot",
{background: "black", desiredSize: new go.Size(5, 5), cursor:"pointer"},
new go.Binding("portId", "portId"),
new go.Binding("alignment", "alignment", go.Spot.parse).makeTwoWay(go.Spot.stringify),
new go.Binding("fromLinkable", "fromLinkable"),
new go.Binding("toLinkable", "toLinkable"),
new go.Binding("fromSpot", "fromSpot", go.Spot.parse).makeTwoWay(go.Spot.stringify),
new go.Binding("toSpot", "toSpot", go.Spot.parse).makeTwoWay(go.Spot.stringify),
new go.Binding('fromMaxLinks', 'fromMaxLinks'),
new go.Binding('toMaxLinks', 'toMaxLinks'),
new go.Binding('background', '', (data)=>{
return data['isHighlighted']? 'yellow': 'black'
}),
{
toolTip: GO('ToolTip',
GO(go.TextBlock, { margin: 4 }, new go.Binding('text', 'name'))
)
}
)
可以不必细看,就是根据数组内定义的位置等信息来实现节点的端口。同时也是为了使用 PortShiftingTool
用于端口移动。
起初结果很满意大致如下。同时端口可以自由移动。
问题发现
后来突发奇想,想在节点上显示该节点的名称。
于是修改了节点的代码,添加了 go.TextBlock
如下:
GO(go.Node, "Spot",
{itemTemplate: nodePortTemplate},
new go.Binding("itemArray", "portArray"),
makeNodeTemplate(type, stretch, size),
GO(go.TextBlock, new go.Binding('text', name))
}
拖入时没有问题
但是当添加了节点后,就会发现文字不见了。
思路和尝试
起初以为是将端口用了 go.Panel
挡住文字了。后来发现定义过了大小,而是 go.TextBlock
确实不见了。
继续研究官网 API 之后,发现对于 itemArray
有这么一句描述。
Replacing this array results all of this panel’s child objects being replaced with a copy of the Panel found in itemTemplateMap for each particular item in the Array.
替换此数组会导致此面板的所有子对象替换为在
itemTemplateMap
中找到的数组中每个特定项的面板副本。
大致意思或许是,使用了 itemArray
和 itemTemplate
后,会把面板原来有的子对象都替换成数组下的特定的模板。
但此时仍有疑问,makeNodeTemplate()
的返回值 go.Picture|go.Shape
明明也是和 itemArray
在同一个 go.Node
节点下的,那么对于图片来说也是这个节点的子对象,但是图片并没有消失。
后来尝试把函数直接换成一个 GO(go.Picture,...)
或者 GO(go.Shape, ...)
发现都依然存在,能够生效。但如果存在两个,则后一个对象在生成端口时,就会不见。情况和上述添加了 go.TextBlock
一致。
后来认为难道是,最前的元素算是这个节点的主元素,所以不归为子对象吗?也就是说作为主元素是不会被替换的吗?关于 isPanelMain
于是做了以下尝试。
GO(go.Node, "Spot",
{itemTemplate: nodePortTemplate},
new go.Binding("itemArray", "portArray"),
//makeNodeTemplate(type, stretch, size),
GO(go.Shape, "Rectangle", {fill:'white'}),
GO(go.Shape, "Rectangle", {desiredSize: new go.Size(10, 10), fill:'red', isPanelMain:true}),
}
发现和预想的不一样,仍然是后面的元素消失了。难道是位置在起作用?
GO(go.Node, "Spot",
{itemTemplate: nodePortTemplate},
new go.Binding("itemArray", "portArray"),
GO(go.TextBlock, {stroke:"red"}, new go.Binding("text", "name")),
makeNodeTemplate(type, stretch, size),
)
于是互换了一下位置进行尝试。发现然如此。
拖入时,并不是没有文字,只是被盖住了。
添加节点后。
只剩下文字了。
那么就可以得出一个可用的结论。
结论
使用 itemArray
和 itemTemplate
确实会对该节点下的子对象产生影响,导致其他子对象不能显示。但是存在一个例外,就是该节点的第一个元素/对象,却是会被保留的。
那么如果我需要既显示图片又显示文字该怎么办?简单来说,使用 go.Panel
把两者包裹起来成为一个对象,将 go.Panel
放置在使用了 itemArray
的 go.Node
下的第一个位置即可。
解决方法如下:
GO(go.Node, "Spot",
{itemTemplate: nodePortTemplate},
new go.Binding('angle').makeTwoWay(),
new go.Binding("itemArray", "portArray"),
GO(go.Panel, "Spot",
makeNodeTemplate(type, stretch, size),
GO(go.TextBlock, {stroke:"red"}, new go.Binding("text", "name")),
)
)
但我并不认为这是一个比较好的方法,只是通过某种方式绕开了限制,或许也可能是我对 itemArray
和 itemTemplate
的使用存在问题。
但是官方 API 并没有对这个特点阐述,导致一开始以为所有的子对象都会被影响替换。