Bug 40788 - Add a custom event to Vue/Vue Islands when the Vue is loaded
Summary: Add a custom event to Vue/Vue Islands when the Vue is loaded
Status: NEW
Alias: None
Product: Koha
Classification: Unclassified
Component: Architecture, internals, and plumbing (show other bugs)
Version: Main
Hardware: All All
: P5 - low enhancement
Assignee: Bugs List
QA Contact: Testopia
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2025-09-10 16:29 UTC by Lucas Gass (lukeg)
Modified: 2025-09-12 01:12 UTC (History)
2 users (show)

See Also:
GIT URL:
Change sponsored?: ---
Patch complexity: ---
Documentation contact:
Documentation submission:
Text to go in the release notes:
Version(s) released in:
Circulation function:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Lucas Gass (lukeg) 2025-09-10 16:29:00 UTC
To make Vue/Vue Islands work with customization from IntranetUserJS we need to add a custom event to tell JS the content is loaded.
Comment 1 David Cook 2025-09-11 00:12:05 UTC
Can you flesh this one out some more?

Are you wanting code in IntranetUserJS to run only after the Vue/Vue Islands have loaded and rendered output? In practice, I think this can be quite challenging because of the reactivity of Vue components. Like you might overwrite a label at page load time, but it can just get changed back during the normal course of operation for the Vue.js.

Are you thinking a custom event that we'd dispatch to the "document" element from the Vue? That might work as a once off, but it might be necessary to have more hooks. I think it gets complicated quickly.

--

I have pondered this one a bit in the past. I had a PHP app with several different Vue apps on the same page, and some third-party Javascript to try to customize the HTML (this was before Vue apps were added to the PHP app). I linked the Vue apps together using a simple event bus (suboptimal but easy enough for this simple case), but I think I ditched the third-party Javascript.

Although the way I did that was by allowing custom alternative templates to be added on a per-instance level for the PHP app. So that different instances of the overall app (like Koha) could have different custom templates.

That comes with a number of its own problems too, of course.
Comment 2 David Cook 2025-09-11 00:14:42 UTC
Do you have any particular use cases in mind? 

I have been thinking that the designs used in IntranetUserJS might need to evolve in a Vue.js world.
Comment 3 Lucas Gass (lukeg) 2025-09-11 13:27:27 UTC
(In reply to David Cook from comment #2)
> Do you have any particular use cases in mind? 
> 
> I have been thinking that the designs used in IntranetUserJS might need to
> evolve in a Vue.js world.

I think you may be right. Simple use case is:

-Prior to 25.05 I was using a IntranetUserJS to add a custom link to the acquisitions menu ( #acquisitions-menu ). After upgrading to 25.05, that jQuery doesn't work consistently.
Comment 4 David Cook 2025-09-12 00:45:42 UTC
(In reply to Lucas Gass (lukeg) from comment #3)
> I think you may be right. Simple use case is:
> 
> -Prior to 25.05 I was using a IntranetUserJS to add a custom link to the
> acquisitions menu ( #acquisitions-menu ). After upgrading to 25.05, that
> jQuery doesn't work consistently.

Ah so that will be bug 38941 changing the acquisitions menu into a Vue.js web component eh...

That's a tricky one. 

I've got some ideas...
Comment 5 David Cook 2025-09-12 00:46:11 UTC
First option which can be used immediately:

I created a simple MutationObserver[1] that added a link to the acquisitions menu consistently. Basically, it just observes if the "acquisitions-menu" adds a child node, and fires when it does. Due to the design of that menu, it's pretty efficient, since that should just happen once.

[1]
const observer = new MutationObserver((mutations) => {
  let acq_menu = document.querySelector("div#acquisitions-menu ul");
  if (acq_menu){
    if ( ! document.querySelector("li#new_list") ){
      let new_list = document.createElement("li");
      new_list.id = "new_list";
      let new_link = document.createElement('a');
	  new_link.textContent = 'Awesome link';
	  new_list.appendChild(new_link);
      acq_menu.appendChild(new_list);
    }
  }
});

observer.observe(document.querySelector("acquisitions-menu"), {
  childList: true
});
Comment 6 David Cook 2025-09-12 00:52:00 UTC
Second option which would take some development...

In theory I think that we could use Vue.js mixins (https://vuejs.org/api/options-composition#mixins) to create and dispatch custom events.

We could update koha-tmpl/intranet-tmpl/prog/js/vue/modules/islands.ts so it would be like "island_created", "island_mounted", etc. And then we could make sure the event had data in it so that the island itself could be located (like one does with a click handler using event.target).

That could be cool.

The usefulness will probably vary depending on the case I imagine. 

But that would just work for our Vue islands. For other Vue components... it gets harder because they get defined in all kinds of different places. Islands are handy because they're all managed through that one islands.ts file.
Comment 7 David Cook 2025-09-12 01:04:04 UTC
I suppose another option could be to make Vue.js menus specifically extensible in some way. But that would probably be difficult to do in a way that made everyone happy.
Comment 8 David Cook 2025-09-12 01:10:06 UTC
Another thought... at the moment IntranetUserJS is loaded via <script></script>

Check out my comment here: https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=38706#c12

If we had the ability to load IntranetUserJs snippets with different attributes, that changes things too. 

For instance, you could change the snippet to also be a "module", and then things can get interesting, as IntranetUserJS gets run *after* islands.esm in terms of DOM order.

That said, I think the imports are done asynchronously so using something event-driven would be better.
Comment 9 David Cook 2025-09-12 01:12:47 UTC
That said, while the above work for acquisitions-menu, for something like http://localhost:8081/cgi-bin/koha/acquisition/vendors/1 I think it could be tougher.

Oh well. Certainly an interesting area to explore...