Looking for a potential workaround to what seems to be an order of execution problem. I'm trying to press a button to enable, and also focus a text field.
I've created a simple page to illustrate this issue:
We have a text input field, an icon as a button, and a numeric display.
Order of events:
When the icon is clicked, it sets the text field's enabled property to True.
On the enabled property, there is a change script which, if the value is true, sets the focus, then increments the numeric counter to prove it ran (Script at end).
This (should) allow us to press the smiley to enable and focus the text field.
What actually happens is the counter increments with no effect on the focus. I have tried a cascading binding, i.e. a diferent property with a change script bound to the enabled field, also to no effect.
If the field is not 'disabled', then the focus can be set fine. Thats not very good in production though, as I need to disable the field!
Is this an issue of timings with the field enabling vs focus trying to be set? And is there a workaround for this?
Thanks!
Alex
if currentValue.value == True:
self.focus()
self.getSibling("NumericEntryField").props.value = self.getSibling("NumericEntryField").props.value + 1
Is it being disabled a security feature (locked behind user roles)?
Property changes happen on the gateway and the onchange is faster than the component actually changing for the client.
So its quite possible this focus event trigger before the component actualy changed clientside
Spot on, it is locked behind security on the enable / disable.
Would this be one of those times where a short delay in the script is the solution?
Or am I somehow able to access the actual change happening to act upon?
You could detect it with js injection, but a delay is probably easier, yet more prone to fail if you try to time it nicely...
I dont like to mess with focus to much
add this markdown to your view and give your input fields a class named focusOnChange
This code works in the front end and will change on every component with the class. if this component changes its disabled atrribute, it will trigger a focus.
(only tested on text input)
(binding)
#make the propName the key to write too in the view.custom
propName = value
code = """<img style='display:none' src='/favicon.ico' onload=\"
const callbackFocus = (mutationList, observer) => {
if(mutationList.some(m => m.attributeName == 'disabled')){
mutationList[0].target.focus();
}
};
const observerClasses = new MutationObserver(callbackFocus);
const elements = document.querySelectorAll('.psc-focusOnChange');
elements.forEach(e => observerClasses.observe(e, { attributes: true, childList: true, subtree: true}));
\"></img>""".replace("\n", "").replace("\t", "")
return code
Thank you very much @victordcq !
I've not yet dipped my toes into the world of js_injection, so really appreciate the example and detail - Ill go have a play now and see how it goes!
I don't usually play with focus either, but the client has asked me so many times 'can't you just' that I've given in and got curious
This works amazing! But it also some crazy wizardy!
If I may ask, whats the purpose of the openDocks empty array that is used for the expression binding? I cant seem to figure out how this prop is involved. Theres the line:
#make the propName the key to write too in the view.custom
propName = value
But I cant also see anywhere that propName is used. Is it just not needed in this example?
Strangely, adapting this to point at a numeric entry field, it works the first time an enable /disable is pressed, and then doesn't work again till the page is refreshed. Nothing is jumping out to me as to why - Any ideas?
Change this to make everything true; this means it will look at its child elements.
And you will have to add the class to the props.containerStyle.classes