Tabris.js Selector API, part 2: Selector clashes

When we create a custom component we want to expose certain properties and methods. By passing in options and hooking into change events, the component can be configured as the caller (the developer who is using our component) sees fit.

However, we do not want to expose the internal workings of our component to the caller. Outside manipulation of these internals could have unexpected results and even break our component.

It is up to us, as creators of a custom component, to prevent this. In terms of selectors this means we must make it impossible for elements within our component to be selected from outside the component itself.

This is in danger of happening when find() or apply() are used. These methods search through all child components of their host for elements that match the selector passed (as opposed to the children() method that only goes one level deep). So the selection might return components inside our custom component that are meant to be internal.

Take the following example from the documentation:

Here we have a button with id primary. But inside our custom component we also have a button with id primary:

This selector widget.find('#primary').set({text: 'blue'}); will make the text of both buttons blue, even though the caller never intended to affect the button inside MyCustomComponent. The caller could not even see that this button had an id primary.

Behind the scenes the find() method recursively calls .children() to filter the host widget’s immediate children, those children’s immediate children and so forth, returning those that match the selector. So we can prevent it from digging through our custom component’s children by overriding the children() method.

When the find() or apply() algorithm calls MyCustomComponent.children(), MyCustomComponent will return an empty WidgetCollection, so MyCustomComponent’s children will not be searched for matches.

@component

Decorating our custom widget class with @component (only available when using TypeScript) overrides the children method implicitly. This decorator can be found in the ‘tabris-decorators’ module.

Selecting child components from within our subclass

But what if we want to legitimately select children of our widget from within our custom component class? Since it has been overridden, calling this.children() will return an empty collection. Since the find and apply methods use the children method, they will also fail to find matches.

In this case we can use the _find(), _apply() and _children() methods. This are protected methods, only available to subclasses of Composite, and must be called from within the class using the this keyword. The methods are identical to their similarly named counterparts, but do not use children()– this function has been disabled, encapsulating our component and protecting it from outside manipulation.

More details can be found using the following resources:
https://docs.tabris.com/latest/selector.html#encapsulation
https://docs.tabris.com/latest/api/Composite.html#_findselector
https://docs.tabris.com/latest/databinding/@component.html#encapsulation