ClientTagProvider.subscribeAsync

com.inductiveautomation.ignition.client.tags.model.ClientTagProvider
subscribeAsync(List<TagPath>, List<TagChangeListener>)

How do I use this method? My class is extending AbstracVisionShape, so I'm using the context that is provided by that extension to get to
context.getTagManager().getDefaultProvider().subscribeAsync

I understand how to build a list of Tag Paths to feed into the subscribe method, but what do I do about the list of TagChangeListeners?

Well, if you're subscribing to tag updates, that would imply that you are going to need a listener. So you would have to build an event listener which implements TagChangeListener and then provide that to the method.

What are you really trying to achieve though? Do you need to listen for tag changes at all?

TagChangeListener is just a functional interface acting as a callback. You provide a list of tag paths and a matching length list of "do this when the tag changes" functions - which could technically all be the same, if you want, since TagChangeEvent includes the path that actually invoked the change.

Yes, I need to listen for tag changes on a dynamic list of tags.
Can I implement TagChangeListener on the class I'm working on already?

Sure - why not? Just pass it as Collections.nCopies(tagpaths.size(), this) or whatever else is appropriate.

Something like this?

List<TagPath> tagPaths = new ArrayList<>();
        for (Object s: sources.getColumnAsList(0)) {
            int endIndex = s.toString().length();
            tagPaths.add(TagPathParser.parse(
                s.toString().substring(0,endIndex) + "_Color"));
        }

List<TagChangeListener> tagListeners = Collections.nCopies(tagPaths.size(), this);

context.getTagManager().getDefaultProvider().subscribeAsync(
                tagPaths, 
                tagListeners);

Sure, that should work. That code to generate the tag paths seems more complex than necessary, but it should work.

Any recommendation to make that less complex would be appreciated as well :grin:
I just started learing Java 3 months ago, so I'm not all that efficient in it.

There, that's less complex now!

List<TagPath> tagsPaths = new ArrayList<>();
for (Object s: sources.getColumnAsList(0)) {
   tagsPaths.add(TagPathParser.parse(s.toString() + "_Color"));

I have the above mentioned code under a property setter called setSources().
When I open a window in Ignititon with this component on it, it complains that context is null. So setSources() runs before the component is handed a context.
How can I prevent any property setters from running before startupComponent is complete?

You cannot stop them. But you can cache the values and apply them after your own component startup.

1 Like

One option, opinions and your mileage may vary, is the stream API:

List<TagPath> tagsPaths = sources.getColumnAsList(0).stream()
            .map(s -> TagPathParser.parseSafe(s.toString() + "_Color"))
            .collect(Collectors.toList()); // or .toList(), if you're compiling against Ignition 8.1.33 or later

Note that Java streams are not a zero-cost abstraction, and the imperative solution with a for loop will pretty much always win in a direct performance comparison. But the stream API has (debatable) advantages in emphasizing "correctness", such as by strongly discouraging side effects and pushing towards "pure" functional operations.

1 Like

How would I go about that?