Skip to content

Proposal: A Core Hook System for a More Modular and Extensible JavaScript #322

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: dev
Choose a base branch
from

Conversation

BernhardBaumrock
Copy link
Contributor

Hi Ryan,

I'm excited to propose a new feature for the core: a JavaScript hook system that mirrors the power and elegance of our beloved PHP hooks.

This module introduces addHookBefore(), addHookAfter(), and event.replace to any JavaScript object or class. It's designed to make our client-side code as modular, extensible, and fun to work with as the PHP side. Imagine third-party modules being able to cleanly and reliably modify the behavior of core UI components, like modals or page tree actions.

I've included a full set of interactive examples (including an Alpine.js demo) in the ProcessJavaScriptHooks module to showcase how it works.

This could be a huge step forward for ProcessWire's client-side architecture. I'd be thrilled for you to take a look and consider it for the core.

Thanks!
Bernhard

PS: You can either checkout this PR directly or install the standalone module that I have created for development here: https://github.com/baumrock/JavaScriptHooks

@poljpocket
Copy link
Contributor

poljpocket commented Jul 13, 2025

Hi @BernhardBaumrock, it's me asking questions again :). You know, I have misunderstood you before in the forums, so I might be wrong again. Let's hope so!

But nonetheless, following question:

Shouldn't this PR need to also include changes to all the JavaScript in the core for this to be useful?

How does your hook system add anything if it's only ever going to be used in code outside of the core? By that, I mean in code you are writing yourself anyway.. code you can just build the modifications in directly without needing hooks. Or just include the single JavaScript class as a library to your codebase.

Or let me put it this way: I feel like this only needs to be in the core if the core actually uses it. Because otherwise, you can just let this be a module you can use to add cool functionality to your own code if needed.

And also if you add some examples of hookable (and useful!) parts of the JavaScript in the core, it would really help your case because you can build actual examples extending the core's code.

@BernhardBaumrock
Copy link
Contributor Author

Hey @poljpocket absolutely valid points. I didn't mention this and it's one of the main reasons for being in the core:

If it is not, the core will never use it. And I think it should.

If it is in the core, then the core will use it and so can any other JS based module. It's been a tremendously helpful pattern for me in RockCommerce, RockGrid, etc.; You'll only understand once you start adopting to it. And it will never happen if it's a module that you have to install first.

I did implement it as a module (RockJavaScriptHooks) first to make sure it works. And it works extremely well!

I truly believe this will have a very positive impact on ProcessWire. Think of it that way: What if ProcessWire didn't have the PHP hook system. Would it work? For sure! Would it be possible to customise things? For sure - one way or another.

The key takeaway is the following for anybody developing anything based on JS for ProcessWire: Want to make something customisable by the user? Just add three underscores and that's it!

Does that answer your questions?

@BernhardBaumrock
Copy link
Contributor Author

BernhardBaumrock commented Jul 13, 2025

Oh, and I disagree with your statement that it only adds value if the core uses it! Think about url hooks. They have not been there until they have been added. And when they have been added nothing in the core was using url hooks. Have they been a valuable addition? Absolutely! We could have kept hooking into the 404 process, but url hooks are so much nicer!

@poljpocket
Copy link
Contributor

Ok, now I think you misunderstood.

What I'm saying is: Shouldn't we modify the JavaScript code in the core to use it and add real-world examples directly in this PR? What does this new addition help if the core JavaScript isn't hookable?

I think your examples don't get the point across. It's cool to have a couple of hello-world examples. But that increment in Alpine.js is only useful if the buttons and the first part of the code is actually part of the core and thus something you won't be able to modify without changing the core JavaScript files.

But here are examples which come to mind:

  • Extend the select field to allow for AJAX-driven dropdown options.
  • Extend the text-tags field to allow for color-coded items.

I can help if you want. Because I think this is very cool and useful too.

@BernhardBaumrock
Copy link
Contributor Author

Hey @poljpocket this sounds great!

I think your examples don't get the point across. It's cool to have a couple of hello-world examples. But that increment in Alpine.js is only useful if the buttons and the first part of the code is actually part of the core and thus something you won't be able to modify without changing the core JavaScript files.

Yeah this was left to the imagination of the user and I thought this was obvious :)

But here are examples which come to mind:
Extend the select field to allow for AJAX-driven dropdown options.
Extend the text-tags field to allow for color-coded items.
I can help if you want. Because I think this is very cool and useful too.

That sounds super cool and helpful! If you could add those it would definitely help to show what the PR is about!

How should we proceed? I think you could just add another PR that makes those components hookable. This would be a great example of 1) how to add hooks to the core (this PR) and 2) how to refactor existing components to add hooks to them and make them more extendable (your PR)

@poljpocket
Copy link
Contributor

I added a draft PR for your PR 😸

baumrock#1

@BernhardBaumrock
Copy link
Contributor Author

Hey @ryancramerdesign thx for having a look. I think it's better to discuss my PR here and I guess your comment baumrock#1 (comment) would also belong in this thread.

Your comment sounds like you still don't see where this would be useful. Think of a datepicker. At the moment we do customisation via the translation system. It mostly works, but I would consider this a bit hacky as we clearly use the translation system for something different than a translation.

Not as long as we talk about locale settings, of course, but what if we wanted to not only translate the datepicker but also modify its behaviour? For example lets say we wanted to add a callback that prevents selecting certain dates. How would we add this to the translation system? Thats just adding three underscores to a method with my PR.

Then the user can do anything. Not just things that we thought of upfront but also things we didn't even think of when developing the module. Thats the power of hooks we all know and we all love. Just not in PHP but in JS.

@poljpocket
Copy link
Contributor

poljpocket commented Jul 24, 2025

Hi everyone. Yes, let's continue the discussion here outside of my PR of Bernhard's PR 😄

@ryancramerdesign I think it's important to point out that this proposal has been misunderstood multiple times by me and others: Just to reiterate, to include this in the core, all JavaScript will need to be changed or rewritten because right now, the code isn't designed at all with hooks in mind. Bernhard's proposal isn't at all how the code would look like because it doesn't actually change any code in the core. If he wants to for example make his RookCommerce module hookable, he can do that right now with this implementation. He can do that without changing a line of code in the core. As long as he isn't relying on any fields from the core that is.

@BernhardBaumrock That's exactly why I said "your examples don't get the point across" time and time again. We should go ahead and create a real-world example. Your proposal with the date picker is perfect: It's something in the core which right now, can't easily be changed using the current codebase and modules won't be able to do this at all unless the module provides a complete replacement of the datepicker component. I'll get rid of my PR-PR and let's focus on that example and get it done. So, this PR should:

  1. change the JS in the core which handles the datepicker component to allow for hookability
  2. add a complete module which will alter the date selectability based on the new hook system

@BernhardBaumrock
Copy link
Contributor Author

Hey @poljpocket thx for the clarification.

Just to reiterate, to include this in the core, all JavaScript will need to be changed or rewritten because right now, the code isn't designed at all with hooks in mind.

I want to clarify as well: All JavaScript in the core CAN stay untouched. This PR will not have any side effects to any existing JS implementations in the core. It will only kick in once a class is "wired" via ProcessWire.wire(...)

This means that, to take advantage of hooks, we CAN refactor existing JS (like for the datepicker), but we don't have to. I think it will help a lot once we decide to and until that happens anybody can try out hooks in JS without any risk or without any obligations. Nobody needs to refactor anything - not in the core, not in personal code.

@poljpocket if you find time to refactor the core datepicker that would be great. I don't think it's necessary to understand what this PR is about. But maybe I'm wrong. For me it is obvious and I thought it would be obvious for anybody as well that has asked himself this question at least once: "Ok, how do I properly make this configurable by the user?"

With hooks the answer is just three underscores away. No messing around with events or callbacks. No additional need for documentation. Only a proven and well known concept that anybody can use and rely on - if he/she wants to.

@poljpocket
Copy link
Contributor

poljpocket commented Jul 24, 2025

I want to clarify as well: All JavaScript in the core CAN stay untouched. This PR will not have any side effects to any existing JS implementations in the core. It will only kick in once a class is "wired" via ProcessWire.wire(...)

It can't because if you look at it, it's not written in any way at all which makes hooks work.

Let me make an example:

function validate($select) {
var $parent = $select.parent(),
$month = $parent.children('.InputfieldDatetimeMonth'),
month = parseInt($month.val()),
$day = $parent.children('.InputfieldDatetimeDay'),
day = parseInt($day.val()),
$year = $parent.children('.InputfieldDatetimeYear'),
year = parseInt($year.val()),
$value = $parent.children('.InputfieldDatetimeValue'),
date = month && day && year ? new Date(year, month - 1, day) : null,
errorClass = 'InputfieldDatetimeError';
if(date && date.getMonth() + 1 != month) {
// day not valid for month
day = '';
$day.val('').addClass(errorClass);
} else {
$day.removeClass(errorClass);
}
$value.val(date && day ? year + '-' + month + '-' + day : '');
}

It is part of the JS code which handles InputfieldDatetime. It validates date selections for the multiple <select> date input type. This code needs to be changed around completely to allow any hook to reject a certain date selection. You can't just throw your cool new ProcessWire.wire() around it and be done.

And as I said before, any code NOT in the core doesn't need hookability as a core concept because you the developer can just add hookability to your code by adding something similar to the Hooks class (also HookEvent and HooksHelper) to the code base.

In that case, this concept then can be offered as a nodejs library or at best a PW module for developers to extend their own JS code with.

@BernhardBaumrock
Copy link
Contributor Author

@poljpocket my point is that merging this PR does not mean that we have to refactor hundreds of core JS implementations. We CAN if we want to and if it adds a benefit, but we don't have to.

Of course, if we don't change any core implementations than they will not be hookable, that's obvious.

to include this in the core, all JavaScript will need to be changed or rewritten because right now, the code isn't designed at all with hooks in mind.

I was just afraid that someone could misinterpret this as that merging my PR means that "all JavaScript will need to be changed/rewritten", which is not the case.

I'm proposing a standard that everybody can use so that we all don't have to reinvent the wheel over and over again by adding custom events that somebody can listen to. Or by adding custom translations somewhere that someone can use to modify the behaviour of a module. Or by offering callbacks somewhere that might not even be documented.

All we need is three underscores and things will be obvious, coherent, easier to maintain and more powerful. With no overhead and no obligations.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy