Highlighting items in a tablerow

So, I’ve gotten pretty far using the examples. Working on searching/highlighting nodes. Needed a solution that scanned not only the node attributes (like key) for the search value (from your examples btw) but now also scans the data in an array which is eventually associated with a table itemarray. Got that working with this code:

function searchNodeFields(obj, safe) {
    if (typeof (obj) == "undefined") return null;
    let rtn = false;
    for (let i in obj) {
        let f = obj[i];
        let k = f.key;
        if (typeof (k) == "undefined") break;
        if (k.toLowerCase().includes(safe.toLowerCase())) {
            rtn = true;
            **f.highlighted = true;**
        }

    }
    if (rtn) return obj;
    else return null;
  
}

function searchDiagram() {  // called by button
    var input = document.getElementById("mySearch");
    if (!input) return;
    myDiagram.focus();

    myDiagram.commit(function (d) {
        if (input.value) {

            var safe = input.value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
            var regex = new RegExp(safe, "i");
            var results = myDiagram.findNodesByExample(
                { key: regex }
            );
            d.highlightCollection(results);

            d.nodes.each(function (n) {
                var nodefields = searchNodeFields(n.data.fields, safe);
                if (nodefields != null) {
                    d.model.set(n.data, "fields", nodefields);
                    n.isHighlighted = true;
                }
            });

        }
        else {

            myDiagram.clearHighlighteds();
        }
    }, "Search");
}

I added an attribute into the array (highlighted) which I set to false by default. You can see that I’m setting that value to true if it contains the string I’m looking for. I’ve verified that my template binding works (by defaulting it to true instead of false), but it seems like that data isn’t making it to the tablerow template or isn’t getting re-evaluated or something because its not changing the color to yellow when highlighted is turned to true

image

image

Is there another way to do this (that perhaps wouldn’t require an attribute in the data itself), and/or is there something else I need to do to tell the table to refresh itself?

There is no predefined way in which an isHighlighted Part is shown. How did you implement that in your node template?

It seems to me that saying a Node is highlighted isn’t what you want anyway. Don’t you want to highlight a particular item in the list, or perhaps multiple items, and not others?

You might want to look at how field selection is implemented in Mapping Selectable Fields of Records Note that selected fields are different also from the Part.isSelected state and Diagram.selection collection, just as I believe highlighted fields would be in your nodes.

I’m actually trying to do both… highlight a node that contains the value I’m searching for, which is working great, and then highlight the item within the node’s table itemarray that matches what I was searching for. So I have this:

function tableRowStroke(h) {
return h ? “yellow” : “white”;
}

),
$(go.Panel, “Table”,
new go.Binding(“itemArray”, “fields”),

                            {
                                //visible: false,
                                defaultAlignment: go.Spot.Left,
                                

                                //name: "LIST",
                                stretch: go.GraphObject.Fill,

                                itemTemplate:
                                    $(go.Panel, "TableRow",


                                        $(go.TextBlock,
                                            {
                                                
                                                column: 0,
                                                stroke: "white",
                                                isMultiline: false,
                                                editable: false,
                                                margin: new go.Margin(3,5,0,3),
                                                font: "12px sans-serif"
                                            },
                                            new go.Binding("text", "shortname"),
                                            **new go.Binding("stroke", "highlighted", tableRowStroke),**
                                        ),
                                       
                                        $("Button",
                                            {
                                                column: 2,
                                                margin: new go.Margin(2, 0, 2, 5),
                                                desiredSize: new go.Size(8, 8),
                                                // set properties on the border Shape of the "Button"
                                                "ButtonBorder.figure": "TriangleRight",
                                                "ButtonBorder.fill": "transparent",
                                                "ButtonBorder.stroke": "grey",
                                                "ButtonBorder.strokeWidth": 3,
                                                // set properties on the "Button" itself used by its event handlers
                                                "_buttonFillOver": "white",
                                                "_buttonStrokeOver": "cyan",
                                                "_buttonFillPressed": "lightgray",
                                                click: function (e, button) { alert(button.findObject("ButtonBorder").stroke); }
                                            },
                                            $(go.TextBlock, "+")
                                        )

                                    )
                            }
                        )

With an array object that looks like this: (Field)
{ key: field.id, caption: field.caption, scope: field.fieldType, dataType: field.dataType, shortname: shortname, highlighted: false };

This is the main data object that I’m binding to the node itself
{ key: id, parent: parentID, scope: getScope(pObject), minOccurs: pObject.minOccurs, maxOccurs: pObject.maxOccurs, path: pObject.path, fields: f, inherited: pObject.inherited };

“fields” is an array of Field (above)

You can see in the screenshot in the original post, that I am setting highlighted to true, for some of the records, then setting the value of the ‘fields’ (in node data) to the new array. Just seems like the table might not be picking up on the change for some reason.

The complete node template is below… but it works great except for this 1 thing which I’m suspicious is simply not ‘refreshing’ something.

var groupWithFieldsTemplate2 =
$(go.Node, “Horizontal”,
{
shadowBlur: 0,
isShadowed: false,
deletable: false,
isTreeExpanded: true
},
new go.Binding(“isShadowed”, “maxOccurs”, shadowConverter),
new go.Binding(“shadowColor”, “scope”, scopeBrushConverter),
$(go.Panel, “Auto”,
{},

                $(go.Shape, "RoundedRectangle",
                    {
                        //fill: "lightgray",
                        fill: "transparent", strokeWidth: 3,
                        stretch: go.GraphObject.Fill,
                        
                        //width: 600
                        name: "SHAPE"

                    },
                    new go.Binding("fill", "", fillConverter).ofObject(),
                    new go.Binding("stroke", "scope", scopeBrushConverter),
                    new go.Binding("strokeWidth", "", inheritedStrokeConverter),
                    new go.Binding("strokeDashArray", "", inheritedDashConverter)
                ),
                $(go.Panel, "Vertical",
                    { stretch: go.GraphObject.Horizontal, defaultAlignment: go.Spot.Left },
                    $(go.Panel, "Horizontal",
                        { stretch: go.GraphObject.Fill, portId: "Fld", toSpot: go.Spot.Left },
                        $("PanelExpanderButton", "LIST",  // the name of the element whose visibility this button toggles
                            {
                                "ButtonIcon.stroke": "white",
                                width: 20
                            },
                            new go.Binding("visible", "fields", function (flds) { return flds.length > 0; })
                        ),
                        $(go.TextBlock,
                            {
                                stroke: "white",
                                margin: new go.Margin(5, 25, 5, 5),
                                verticalAlignment: go.Spot.Top,
                                stretch: go.GraphObject.Horizontal,
                                overflow: go.TextBlock.OverflowEllipsis,
                                font: " bold 12px sans-serif"
                            },
                            new go.Binding("text", "key")
                        )

                    ),

                    // the collapse/expand area (will be mapped template)

                    $(go.Panel, "Auto",
                        {
                            stretch: go.GraphObject.Fill, visible: false,
                            name: "LIST"
                        },
                       
                        $(go.Shape, "Rectangle",
                            {
                                fill: "black", 
                                stretch: go.GraphObject.Fill,
                                name: "SHAPE2"
                            }
                        ),
                        $(go.Panel, "Table",
                            new go.Binding("itemArray", "fields"),

                            {

                                defaultAlignment: go.Spot.Left,
                                stretch: go.GraphObject.Fill,

                                itemTemplate:
                                    $(go.Panel, "TableRow",


                                        $(go.TextBlock,
                                            {
                                                
                                                column: 0,
                                                stroke: "white",
                                                isMultiline: false,
                                                editable: false,
                                                margin: new go.Margin(3,5,0,3),
                                                font: "12px sans-serif"
                                            },
                                            new go.Binding("text", "shortname"),
                                            new go.Binding("stroke", "highlighted", tableRowStroke),
                                        ),

                                        $("Button",
                                            {
                                                column: 2,
                                                margin: new go.Margin(2, 0, 2, 5),
                                                desiredSize: new go.Size(8, 8),
                                                // set properties on the border Shape of the "Button"
                                                "ButtonBorder.figure": "TriangleRight",
                                                "ButtonBorder.fill": "transparent",
                                                "ButtonBorder.stroke": "grey",
                                                "ButtonBorder.strokeWidth": 3,
                                                // set properties on the "Button" itself used by its event handlers
                                                "_buttonFillOver": "white",
                                                "_buttonStrokeOver": "cyan",
                                                "_buttonFillPressed": "lightgray",
                                                click: function (e, button) { alert(button.findObject("ButtonBorder").stroke); }
                                            },
                                            $(go.TextBlock, "+")
                                        )

                                    )
                            }
                        )
                    )

                )
            ),
            $(go.Panel, "Vertical", { height: 15, width: 15, margin: 1 },
                $("TreeExpanderButton", { alignment: go.Spot.Right, alignmentFocus: go.Spot.Left })
            )

        );

I had to comment out some of your bindings that used unknown conversion functions, but everything seems to change color and update when I modify the “highlighted” property on a field descriptor object:

    myDiagram.model.commit(m => {
      const fd = m.nodeDataArray[0].fields[1];
      m.set(fd, "highlighted", !fd.highlighted);
    });

Yep, that’s how I tested to make sure my binding was correct. So if I start out with highlighted = true, it changes the color. But when I change it programmatically, in this code, it doesn’t, which is why I think I must be missing a step in the data update process for a table panel that is bound to an itemArray of the nodes bound data.

 function searchNodeFields(obj, safe) {
        if (typeof (obj) == "undefined") return null;
        let rtn = false;
        for (let i in obj) {
            let f = obj[i];
            let k = f.key;
            if (typeof (k) == "undefined") break;
            if (k.toLowerCase().includes(safe.toLowerCase())) {
                rtn = true;
                f.highlighted = true;
            }

        }
        if (rtn) return obj;
        else return null;

    }

    function searchDiagram() {  // called by button
        var input = document.getElementById("mySearch");
        if (!input) return;
        myDiagram.focus();

        myDiagram.commit(function (d) {
            if (input.value) {

                var safe = input.value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
                var regex = new RegExp(safe, "i");
                var results = myDiagram.findNodesByExample(
                    { key: regex }
                );
                d.highlightCollection(results);

                d.nodes.each(function (n) {
                    var nodefields = searchNodeFields(n.data.fields, safe);
                    if (nodefields != null) {
                        d.model.set(n.data, "fields", nodefields);
                        n.isHighlighted = true;
                    }
                });
                // expand the tree where nodes were found
                var s = document.getElementById("foundKeys");
                while (s.length > 0)
                    s.remove(0);
                d.nodes.each(function (n) {
                    if (n.isHighlighted) {

                        var o = document.createElement("option");
                        o.text = n.data.key;
                        s.add(o);

                        var pKey = n.data.parent;

                        while (typeof(pKey) != "undefined" && pKey != null) {
                            var nx = myDiagram.findNodeForKey(pKey);
                            if (nx != null) {
                                nx.expandTree();
                                pKey = nx.data.parent;
                            }
                        }

                    }
                });

            }
            else {

                myDiagram.clearHighlighteds();
            }
        }, "Search");
 

    }

Note how Walter’s code is using model.set in order to change the value of "highlighted". This is necessary. When changing node or item data, which is just a simple JavaScript object, GoJS has no way of knowing that it has changed.

The way to inform GoJS is to use model.set, passing in the data object (not the Node or Panel), then the string property name, then the value. Assuming that f in your method is data, you can write:’

myDiagram.model.set(f, "highlighted", true);

Instead of

f.highlighted = true;

And it should do what you want.

Hi simon, so, what I think I’m doing is creating a new array from the old array (thats what the function searchNodeFields actually does), then I use model.set to set the “fields” field in the model, to the new array.

d.nodes.each(function (n) {
                    var nodefields = searchNodeFields(n.data.fields, safe);
                    if (nodefields != null) {
                        d.model.set(n.data, "fields", nodefields);
                        n.isHighlighted = true;

And all of this is wrapped with a Diagram.commit( …)

I will try re-arranging my code to get it a little closer to what I’m seeing Walter doing

Did you have a question?

Are you treating the data as immutable? Is that why you are copying the array? In that case I think your code is OK.

Well, I refactored the code to align to your example, and I got it working.

 d.nodes.each(function (n) {

                    if (n.data.fields !== undefined) {
                        n.data.fields.forEach(function (f) {
                            //console.log(f.key);
                            let k = f.shortname;
                            if (k === undefined) k = f.key;
                            if (k.toLowerCase().includes(safe.toLowerCase())) {
                                d.model.set(f, "highlighted", true);
                                n.isHighlighted = true;
                            }
                        });

                    }

(thought I had tried this, but obviously not) Thanks for the direction folks!