# Refresh the resources in the Perspective Component Registry

**URL:** <https://forum.inductiveautomation.com/t/refresh-the-resources-in-the-perspective-component-registry/104537>\
**Category:** Module Development\
**Tags:** perspective\
**Created:** [April 1, 2025, 5:15pm UTC](https://forum.inductiveautomation.com/t/refresh-the-resources-in-the-perspective-component-registry/104537 "2025-04-01T17:15:12Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![kgamble](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/kgamble/32/8019_2.png) [@kgamble](https://forum.inductiveautomation.com/u/kgamble)\
**Post date:** [April 1, 2025, 5:15pm UTC](https://forum.inductiveautomation.com/t/refresh-the-resources-in-the-perspective-component-registry/104537/1 "2025-04-01T17:15:12Z")

</div>

I have a module that needs to execute some JS on the startup of every perspective page, because its pulling in some additional CSS that isn't located in the `themes` directory.

I managed to copy some strategies in @bmusson's [embr modules](https://github.com/mussonindustrial/embr)

Where essentially on startup of the module, I iterate through all of the components in the component registry, add my resource as a dependency, and then when I shut down I remove that reference as well. This forces the page to always load my content, without _my_ component having to be on the page (Because I dont have one!)

I recognize this is a bit hacky, and side note, it would be ideal for module developers to have a way to load in their own browser resources into Perspective overall...

Regardless, I noticed that when I do this, it only actually adds the resource _IF_ I add a component as well. Seems that maybe this is triggering some type of "refresh" on the actual registry that causes it to show up? If I dont, my resource wont show up in the client until after a successful project scan happens when a view is modified, or if I manually restart the Perspective module.

I really hate this solution of making a fake component just for the sake of triggering this "refresh". Is there any _less_ hacky ways to do what I am trying to do here?

```java
 public static final Set<BrowserResource> BROWSER_RESOURCES = Set.of(
            new BrowserResource(
                    "example-bootstrap",
                    "/res/browser-resource-example/bootstrap.js",
                    BrowserResource.ResourceType.JS));

@Override
public void startup(LicenseState activationState) {

    PerspectiveContext perspectiveContext = PerspectiveContext.get(context);
    this.componentRegistry = perspectiveContext.getComponentRegistry();

    this.modifiedComponents = new ArrayList<>();
    ComponentRegistryUtils.addResourcesTo(this.componentRegistry, BROWSER_RESOURCES,
            component -> {
                boolean matches = component.moduleId().equals(PerspectiveModule.MODULE_ID);
                if (matches) {
                    modifiedComponents.add(component);
                }
                return matches;
            });
    
    // This is cursed....
    ComponentDescriptor fakeComponent = ComponentDescriptorImpl.ComponentBuilder.newBuilder()
            .setId("resource.fake.component")
            .setResources(BrowserResourceGatewayHook.BROWSER_RESOURCES)
            .build();
        
    this.componentRegistry.registerComponent(fakeComponent);
    this.componentRegistry.removeComponent(fakeComponent.id());
}

```

Here is a link to the repo with this example in it: [An Example module for showing loading a browser resource in perspective without components](https://github.com/keith-gamble/ignition-browser-resource-example-module)

EDIT: I realized I should have also thrown in what `addResourcesTo` is really doing, here is what it ends up calling for each descriptor and resource

```java
private static void addBrowserResource(ComponentDescriptor descriptor, BrowserResource resource) {
    try {
        Set<BrowserResource> browserResources = new HashSet<>(descriptor.browserResources());
        browserResources.add(resource);
        Field field = ComponentDescriptorImpl.class.getDeclaredField("browserResources");
        field.setAccessible(true);
        field.set(descriptor, browserResources);
    } catch (NoSuchFieldException | IllegalAccessException e) {
        logger.error("Failed to add browser resource {} to descriptor {}", resource.name, descriptor.id(), e);
        throw new RuntimeException("Failed to add browser resource", e);
    }
}

```

---

<div class="post-metadata">

**Author:** ![bmusson](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/bmusson/32/87530_2.png) [@bmusson](https://forum.inductiveautomation.com/u/bmusson)\
**Post date:** [April 1, 2025, 5:23pm UTC](https://forum.inductiveautomation.com/t/refresh-the-resources-in-the-perspective-component-registry/104537/2 "2025-04-01T17:23:23Z")

</div>

> [@kgamble](#):
>
> I recognize this is a bit hacky, and side note, it would be ideal for module developers to have a way to load in their own browser resources into Perspective overall...

+1

The stuff I have to do in Embr to ensure my JavaScript is always loaded is way more difficult than it should be.

---

<div class="post-metadata">

**Author:** ![paul-griffith](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/paul-griffith/32/37249_2.png) [@paul-griffith](https://forum.inductiveautomation.com/u/paul-griffith)\
**Post date:** [April 7, 2025, 5:57pm UTC](https://forum.inductiveautomation.com/t/refresh-the-resources-in-the-perspective-component-registry/104537/3 "2025-04-07T17:57:33Z")

</div>

Y'all need to make a compelling presentation to our sales engineering department so they can push stuff like this in front of the right people.

I've long held that Webdev should have some better tie-in to Perspective - if we expose a hook that Webdev can use we're probably going to (implicitly or explicitly) also open a path for other modules to use.

---

<div class="post-metadata">

**Author:** ![kgamble](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/kgamble/32/8019_2.png) [@kgamble](https://forum.inductiveautomation.com/u/kgamble)\
**Post date:** [April 7, 2025, 5:58pm UTC](https://forum.inductiveautomation.com/t/refresh-the-resources-in-the-perspective-component-registry/104537/4 "2025-04-07T17:58:45Z")

</div>

> [@paul-griffith](#):
>
> Y'all need to make a compelling presentation to our sales engineering department so they can push stuff like this in front of the right people.

I can make this happen, and I’ll also take it as a “well, there’s no way to do it right now” lol

---

<div class="post-metadata">

**Author:** ![bmusson](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/bmusson/32/87530_2.png) [@bmusson](https://forum.inductiveautomation.com/u/bmusson)\
**Post date:** [April 7, 2025, 7:14pm UTC](https://forum.inductiveautomation.com/t/refresh-the-resources-in-the-perspective-component-registry/104537/5 "2025-04-07T19:14:16Z")

</div>

> [@paul-griffith](#):
>
> Y'all need to make a compelling presentation to our sales engineering department so they can push stuff like this in front of the right people.

The backbone of Ignition's module system are its hooks. We have hooks into the gateway, the designer, and Vision client, but we don't have any hooks into the Perspective client. We have the ability to add components to Perspective, but that's not a substitute for a "real" hook.

I'd like a way to specify a set of [BrowserResource](https://files.inductiveautomation.com/sdk/javadoc/ignition81/8.1.33/com/inductiveautomation/perspective/common/api/BrowserResource.html)'s to be loaded by the Perspective client on startup.

My suggestion is the introduction of a `ResourceRegistry` avaiable through `PerspectiveContext.getResourceRegistry()`, which would provide a method for registering `BrowserResources` that are unconditionally loaded by the client **after** `PerspectiveClient.js` and `PerspectiveComponents.js`.

When these resources load, it would give us a place to run code in Perspective that has side-effects on the session as a whole. For examples on session side-effects, see Periscope:

1. Periscope registers a client-wide connection handler for receiving `runJavaScript` messages. These messages are not tied to a single component, but instead the whole page.
2. Periscope adds a new React root on top of Perspective for displaying Toasts. This allows Toasts to be always be available and on top of the Perspective session (again, this is a page feature, not a component feature).

These features are "_possible_" today, but only using hacks which force `BrowserResources` to load regardless of what components are used in the project.

> [@paul-griffith](#):
>
> if we expose a hook that Webdev can use we're probably going to (implicitly or explicitly) also open a path for other modules to use.

This suggestion allows for a bridge between WebDev and Perspective, by providing a method for registering WebDev resources with Perspective's `ResourceRegistry`.

For example, a WebDev resource that provides a CSS file could be added to the `ResourceRegistry`. When the Perspective client starts, the resource would be added to the head of the document and the WebDev endpoint would be queried, loading the CSS into the session.
