I can`t add components created with styled of #mui/material/styles to observable objects. is there any configuration required to fix this?
Uncaught RangeError: Maximum call stack size exceeded
at new ObservableObjectAdministration (mobx.esm.js:4402:42)
at asObservableObject (mobx.esm.js:4829:13)
at asDynamicObservableObject (mobx.esm.js:3267:12)
at Function.object (mobx.esm.js:1317:155)
Sandbox
For such cases you can use observable.ref (docs), because you basically only want to track reassignments, you don't really want to make the whole object (or in this case even Component) observable.
So just change it to:
#observable.ref
public component: React.ReactNode = null;
Related
I'm using InputSystem.EnhancedTouch for my game and I have a few buttons that are available through UI Elements. When I click on the UI Elements buttons it activates the InputSystem thus creating a bad experience. I want to filter out all the InputSystem.EnhancedTouch events that come through UI.
TL;DR - I want UI Elements to block InputSystem.EnhancedTouch events from triggering
I have found quite some resources but nothing really works. Unity and some other people say to use EventSystem.current.IsPointerOverGameObject but it doesn't work and throws the following warning(I think this is meant to work with normal input only, not enhanced one)
I have tried a few other solutions such as UIDocument.rootVisualElement.panel.Pick or EventSystem.current.RaycastAll but nothing seems to work, or return any consistent data that can be used.
InputSystem.EnhancedTouch binding
private void Awake()
{
EnhancedTouchSupport.Enable();
Touch.onFingerDown += OnFingerDown;
Touch.onFingerMove += OnFingerMove;
Touch.onFingerUp += OnFingerUp;
}
I am using ag-grid/ag-grid-angular to provide an editable grid of data backed by a database. When a user edits a cell I want to be able to post the update to the backend service and if the request is successful update the grid and if not undo the user's changes and show an error.
I have approached this problem from a couple different angles but have yet to find the solution that meets all my requirements and am also curious about what the best practice would be to implement this kind of functionality.
My first thought was to leverage the cellValueChanged event. With this approach I can see the old and new values and then make a call to my service to update the database. If the request is successful then everything is great and works as expected. However, if the request fails for some reason then I need to be able to undo the user's changes. Since I have access to the old value I can easily do something like event.node.setDataValue(event.column, event.oldValue) to revert the user's changes. However, since I am updating the grid again this actually triggers the cellValueChanged event a second time. I have no way of knowing that this is the result of undoing the user's changes so I unnecessarily make a call to my service again to update the data even though the original request was never successful in updating the data.
I have also tried using a custom cell editor to get in between when the user is finished editing a cell and when the grid is actually updated. However, it appears that there is no way to integrate an async method in any of these classes to be able to wait for a response from the server to decide whether or not to actually apply the user's changes. E.g.
isCancelBeforeStart(): boolean {
this.service.updateData(event.data).subscribe(() => {
return false;
}, error => {
return true;
});
}
does not work because this method is synchronous and I need to be able to wait for a response from my service before deciding whether to cancel the edit or not.
Is there something I am missing or not taking in to account? Or another way to approach this problem to get my intended functionality? I realize this could be handled much easier with dedicated edit/save buttons but I am ideally looking for an interactive grid that is saving the changes to the backend as the user is making changes and providing feedback in cases where something went wrong.
Any help/feedback is greatly appreciated!
I understand what you are trying to do, and I think that the best approach is going to be to use a "valueSetter" function on each of your editable columns.
With a valueSetter, the grid's value will not be directly updated - you will have to update your bound data to have it reflected in the grid.
When the valueSetter is called by the grid at the end of the edit, you'll probably want to record the original value somehow, update your bound data (so that the grid will reflect the change), and then kick off the back-end save, and return immediately from the valueSetter function.
(It's important to return immediately from the valueSetter function to keep the grid responsive. Since the valueSetter call from the grid is synchronous, if you try to wait for the server response, you're going to lock up the grid while you're waiting.)
Then, if the back-end update succeeds, there's nothing to do, and if it fails, you can update your bound data to reflect the original value.
With this method, you won't have the problem of listening for the cellValueChanged event.
The one issue that you might have to deal with is what to do if the user changes the cell value, and then changes it again before the first back-end save returns.
onCellValueChanged: (event) => {
if (event.oldValue === event.newValue) {
return;
}
try {
// apiUpdate(event.data)
}
catch {
event.node.data[event.colDef.Field] = event.oldValue;
event.node.setDataValue(event.column, event.oldValue);
}
}
By changing the value back on node.data first, when setDataValue() triggers the change event again, oldValue and newValue are actually the same now and the function returns, avoiding the rather slow infinite loop.
I think it's because you change the data behind the scenes directly without agGrid noticing with node.data = , then make a change that agGrid recognises and rerenders the cell by calling setDataValue. Thereby tricking agGrid into behaving.
I would suggest a slightly better approach than StangerString, but to credit him the idea came from his approach. Rather than using a test of the oldValue/newValue and allowing the event to be called twice, you can go around the change detection by doing the following.
event.node.data[event.colDef.field] = event.oldValue;
event.api.refreshCells({ rowNodes: [event.node], columns: [event.column.colId] });
What that does is sets the data directly in the data store used by aggrid, then you tell it to refresh that grid. That will prevent the onCellValueChanged event from having to be called again.
(if you arent using colIds you can use the field or pass the whole column, I think any of them work)
I've got a GWT 2.4 app where I'm "swapping views" by switching out one Composite widget on the RootPanel for another, using the usual RootPanel.get().clear() and RootPanel.get().add(newWidget) to remove and add, respectively.
The first composite widget contains a PasswordTextBox. It listens for the Enter keypress, which triggers the swap. Nothing too fancy:
getDisplay().getPasswordBoxForKeyPresses().addKeyPressHandler(new KeyPressHandler() {
public void onKeyPress(KeyPressEvent event) {
if (event.getNativeEvent().getKeyCode() == KeyCodes.KEY_ENTER) {
swapWidgets(); // clear RootPanel and add new widget
}
}
});
The problem is that there is a DOM memory leak: after RootPanel.get().clear() is called, the old composite widget is stuck in the detached DOM tree because the HTMLInputElement for the PasswordTextBox has some strange reference to it which I cannot identify.
Update:
I took the helpful advice below, compiled at style=detailed and started trying to drive down the tree to look a reference to the element in JS. I'm pretty new with GWT, so it still isn't obvious to me what's going on. So starting with the second line in the retaining tree, I can see that lastEvent in _2 contains the nativeKeyTarget listed at the top of tree. But where do I go from there?
I tracked the reference down to SmartGWT. It tracks the last click event within ISC_Core.js. Further questions are
How will this behavior further affect memory usage in my app?
Can this behavior be bypassed if need be?
But those questions are for another post!
Looks like you may not be tracking the handler registrations which will cause memory leaks, prevent objects from being recovered, and cause phantom event captures.
In pure GWT, it looks like this
// class member
HandlerRegistration reg;
// save for recovery
reg = getDisplay().getPasswordBoxForKeyPresses().addKeyPressHandler(...);
public void onDetatch() {
// recover memory
reg.removeHandler();
reg = null;
}
GXT has a nice grouping feature to prevent registration sprawl, it looks like this
// class member
GroupingHandlerRegistration regs = new GroupingHandlerRegistration();
// save for later recovery
regs.add( getDisplay().getPasswordBoxForKeyPresses().addKeyPressHandler(...) );
// recover memory
regs.removeHandler();
Source code for GroupingHandlerRegistration
Regarding the BoilerplateJs example, how should we adjust those modules to be intercommunicate in such a way once the user done any change to one module, the other related modules should be updated with that change done.
For example, if there is a module to retrieve inputs from user as name and sales and another module to update those retrieved data in a table or a graph, can you explain with some example ,how those inter connection occurs considering event handling?
Thanks!!
In BoilerplateJS, each of your module will have it's own moduleContext object. This module context object contains two methods 'listen' and 'notify'. Have a look at the context class at '/src/core/context.js' for more details.
The component that need to 'listen' to the event, should register for the event by specifying the name of the event and callback handler. Component that raise the event should use 'notify' method to let others know something interesting happened (optionally passing a parameter).
Get an update of the latest BoilerplateJS code from GitHub. I just committed changes with making clickCounter a composite component where 'clickme component' raising an event and 'lottery component' listening to the event to respond.
Code for notifying the Event:
moduleContext.notify('LOTTERY_ACTIVITY', this.numberOfClicks());
Code for listening to the Event:
moduleContext.listen("LOTTERY_ACTIVITY", function(activityNumber) {
var randomNum = Math.floor(Math.random() * 3) + 1;
self.hasWon(randomNum === activityNumber);
});
I would look at using a Publish-Subscribe library, such as Amplify. Using this technique it is easy for one module to act as a publisher of events and others to register as subscribers, listening and responding to these events in a highly decoupled manner.
As you are already using Knockout you might be interested in first trying Ryan Niemeyer's knockout-postbox plugin first. More background on this library is available here including a demo fiddle. You can always switch to Amplify later if you require.
I've an Async TreePanel that uses an RPC proxy to load data from server. I want to reload a node by using:
this.treeLoader.loadChildren(nodeModel);
Then, I want the loaded tree node to become expanded. I tried to:
treePanel.setExpanded(nodeModel, true, false);
but the first call is asynchronous so the "setExpanded" happens before the nodes get loaded.
A solution would be to use a LoadListener on the treeLoader and expand the node after it's children are loaded but the listener's loaderLoad(..) method can't know directly the reason for the reload: maybe the user expanded a node and this triggered the reload or maybe the user clicked on a menu option to reload the node.
Is there any way to improve this so it's easier to trigger the node expand after the user wants to reload a node?
Thanks.
Try removing the listener in the loaderLoad method, as well as in the loaderLoadException to avoid a leak
I suggest to store the node that was selected to be reloaded (add a onClick listener to the thee). Than in the loaderLoad check if the stored object equals the parent of loaded node:
loader.addLoadListener(new LoadListener() {
#Override
public void loaderLoad(LoadEvent loadEvent) {
ModelData parent = loadEvent.getConfig();
if(parent.equals(storedObject) {
// your code here