Closed zJaaal closed 7 months ago
We know that a Proxy needs to be created using new Proxy(target, handler)
this means that we have two objects that in theory has the same prototype but one with a Proxy in the middle and one without one. So in order to hit the handler of the proxy we need to somehow inject the Proxied Element/Node instead of the original one.
Due to the default behavior of DOM Nodes and DOM Elements, we cannot inject Proxied Elements/Nodes, because the browser engine manage those Objects as special and embedded ones. This means that any attempt to append/replace a node/element with a proxied one is rejected by the engine.
One approach that I found several times while investigating how this works, is to create custom elements that controls the get/set of their values. I'm not really sure if it is an optimal approach right now but can be a point to explore if needed.
Proxy is not a way because of how the engine works at the moment. This can change in the future, but right now is kind of impossible.
After some attemps, is clear the element.value = "some" doesnt trigger any event. So we have some options
element.value = "some"
dispatchEvent(InputEvent)
So this trigger the InputEvent and run the callback inside EventListener This implies re-write a lot of code. It's not convenient
const targetNode = dojo.byId("search");
const config = { attributes: true, childList: true, subtree: true };
const callback = function(mutationsList, observer) { for(const mutation of mutationsList) { if(mutation.type === 'attributes' && mutation.target === targetNode) {
// Write code here
}
}
};
const observer = new MutationObserver(callback); observer.observe(targetNode, config);
This approach seems works, but is cost and doest work good with dojo.byId().
Needs reseach but probably this is not the way
Object.defineProperty
Approach).To make custom fields functional in Angular without breaking legacy code, we can use the Object.defineProperty
approach.
dojo.byId()
method.const fields = Object.values(<%= fieldJson %>);
const bodyElement = document.querySelector('body');
fields.forEach(({ variable, value }) => {
const input = document.createElement('input');
input.setAttribute('type', 'hidden');
input.setAttribute('name', variable);
input.setAttribute('id', variable); // Use the variable as the id for the input, as we did in the previous edit content.
input.setAttribute('dojoType', 'dijit.form.TextBox'); // Required for Dojo to recognize the input as a widget
input.setAttribute('value', value);
bodyElement.appendChild(input);
});
dojo
is loaded, we redefine the property value
for each field:fields.forEach(({ variable }) => {
// This code copies the original descriptor from the HTMLInputElement prototype.
const valueDescriptor = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'value');
const input = dojo.byId(variable);
Object.defineProperty(input, 'value', {
get: () => valueDescriptor.get.apply(this),
set: function(value) {
// We can emit custom events in Angular to notify changes.
console.log("Something triggered the setter", {
variable,
value
});
valueDescriptor.set.apply(this, [value]);
}
});
});
This approach works with: dojoInputElement.value="new Value"
.
dojoInputElement.setValue("New Value")
.https://github.com/dotCMS/core/assets/72418962/09d9d457-0cd5-4fac-b2c9-216ececaa947
P.S.: I added those console logs without modifying the .vtl
code. However, we can emit an event to Angular instead.
Parent Issue
https://github.com/dotCMS/core/issues/25445
Task
Make research of how to communicate Angular with Custom Field Iframe using input with hidden type and an strategy to dispatch events from the Iframe to the Angular side.
Timebox: 2h
Proposed Objective
Technical User Experience
Proposed Priority
Priority 3 - Average