documentationfor yFiles for HTML 2.6

Filtering: Hiding Graph Elements

The readability of complex diagrams can often be increased by hiding the parts of the graph which are currently less important for the viewer. One concept to reach this goal is filtering.

Detailed graph vs. filtered graph
Unfiltered graph
Filtered graph

Filtering is achieved by defining conditions which must be fulfilled for graph elements to show up. Technically, the original graph is wrapped in a FilteredGraphWrapper which only shows items fulfilling these conditions. Elements which do not fulfill the conditions (and dependent elements, e.g. edges ending at a hidden node) are hidden. To be hidden not only means they are not displayed, but they are not even contained in the graph’s item collections. Note that the original graph is not altered.

Elements which depend on other elements are also hidden if the element they depend on is hidden. For example, an edge which fulfills the condition for being shown will still be hidden if its source or target node is hidden.

Labels and ports cannot be filtered explicitly. They will be shown or hidden along with their owner.

A special kind of filtering is the concept of collapsing and expanding group nodes which is shown in chapter Folding.

Working with Filtering

Filtering is achieved using a special IGraph implementation, the FilteredGraphWrapper. The FilteredGraphWrapper is a decorator for another IGraph instance. The decorated graph is referred to as the full graph or wrapped graph. You need to pass it as a constructor argument. Afterwards you can access it via the read-only wrappedGraph property.

The FilteredGraphWrapper exposes only a subset of the items in its wrapped graph. The elements to be either exposed or hidden are determined by two predicate functions, one for nodes and one for edges. You need to pass the predicate functions as constructor arguments. Afterwards you can access them via the read-only properties nodePredicate and edgePredicate. Dependent elements are exposed only if the elements they depend on are exposed. Node labels, for example, are only exposed if their owner is exposed. Also, edges ending at hidden nodes will be hidden, too, even if the edge predicate returns true for them.

Setting up a FilteredGraphWrapper
let levelToBeShown
const graph = graphComponent.graph
const wrapper = new FilteredGraphWrapper(
  // the original graph to wrap
  graph,
  // only show nodes whose level is above a certain value
  (node) => node.tag.level > levelToBeShown,
  // show all edges (actually all edges whose source and target is shown)
  (edge) => true
)
// set the filtered graph as the graph to work on
graphComponent.graph = wrapper
let levelToBeShown: number
const graph = graphComponent.graph
const wrapper = new FilteredGraphWrapper(
  // the original graph to wrap
  graph,
  // only show nodes whose level is above a certain value
  (node: INode): boolean => node.tag.level > levelToBeShown,
  // show all edges (actually all edges whose source and target is shown)
  (edge: IEdge): true => true
)
// set the filtered graph as the graph to work on
graphComponent.graph = wrapper

The FilteredGraphWrapper exposes a filtered view of the wrapped graph, i.e. it exposes a real subset (not a copy) of the elements of the original graph:

  • Elements of the filtered graph are referentially equal to their corresponding elements in the original graph.
  • When a graph element is created in the filtered graph, this change is immediately reflected in the original graph.
  • When a graph element is created in the original graph, it will show up immediately in the filtered graph if (and only if) it meets the conditions for being shown.
  • Creating a node in the filtered graph will not have an effect on the filtered graph if the newly created node does not fulfill the conditions for being shown. The node will be created in the original graph, though. The same applies to edges.
  • Graph events from the original graph will be forwarded if and only if the associated element is exposed by the filtered graph.
  • If you try to modify or remove an element in/from the filtered graph which is present in the original graph but not in the filtered graph, you will get an exception.
  • Modifications or removal of graph elements in/from the original graph are immediately reflected in the filtered graph if the modified element is shown.

The filtering conditions are only evaluated upon creation of the FilteredGraphWrapper instance or for newly created elements. To update the graph after changes in the conditions, you need to explicitly trigger a re-evaluation:

nodePredicateChanged(): void
Re-evaluates the predicate for all nodes. This also includes re-evaluation for incident edges of nodes whose visibility has changed.
edgePredicateChanged(): void
Re-evaluates the predicates for all edges.
nodePredicateChanged(node: INode): void
Re-evaluates the predicate for the given node. This also includes re-evaluation for incident edges whose visibility might be affected.
edgePredicateChanged(edge: IEdge): void
Re-evaluates the predicate for the given edge.

If a node becomes visible after a predicate change, a NodeCreated event is dispatched as if the node was newly created (see also Reacting to Graph Changes). Similarly, NodeRemoved events are dispatched for all nodes which become hidden after a predicate change. The same applies to all other graph elements.