So, I have an API that uses Swagger for it's documentation, swagger generates a ReactJs webpage to document and test the API endpoints. I know pretty much nothing about React architecture, So I don't exactly know what is causing this problem:
Before using any endpoint, you have to request a authentication Token, using another endpoint that performs a POST request to another API, that returns the Bearer Token.
Everytime I start the API for test purposes i have make the authorization request "manually" by putting the json in the textarea:
Then I have to grab the token in the response, open the Authorization modal, and paste the token in the input:
And I do that so many times. So i decided to create a little script in JS to make this process to me when i start the API Debugging. And that implies that the script changes the textarea/input values on the swagger page. When the value is changed, you can see it on the UI, but once the script simulates another action, like submitting the request, the changed value returns back to the old/default one.
Looks like it needs user interaction to update the page state?? i don't quite get it, i also tried after changing the values, trigger every change/update js event that i know, but didn't work either.
What exactly React is doing here?
is there a way to fix this?
EDIT: Example of what im doing:
I've installed React Developer Tools as Ray Hatfield suggested, looks like the input is indeed a React Component
Im trying to update the state after I change the value:
But still doesn't work
This is more of a comment than an answer, but…
Is your script changing the textarea value via direct DOM manipulation? e.g. document.querySelector('textarea').value = json? If so, it's likely that React doesn't see it because data/state generally flows downward into the DOM, not upward from the DOM.
My guess is that there's a React component that renders the textarea and registers a change listener on it. Change events from the textarea trigger a state update in the component, which rerenders the textarea to reflect the updated state.
You indicated that you've fired the change event, which seems like it should work given the scenario above. It's possible, though less likely, that the update is triggered via a keydown or keypress.
What I would suggest is that you install the React devtools in your browser and use the "Components" view to inspect the textarea component. It might give you some insights into the state of the component, what events it's listening for, etc., and it would let you see what (if anything) changes when you run your script.
It also includes a button that will open the source code for the selected component. (In minimized apps without sourcemaps it might be impenetrable, but it's worth a look.)
Alright, finally got it, to solve the React component not rerendering, i had to use Javascript's PropertyDescriptor to get the native value setter of the input elements:
var ValueSetter = Object.getOwnPropertyDescriptor(
window.HTMLInputElement.prototype,
"value"
).set;
Then I called the ValueSetter Method passing the Node of the Input Element and it's new value:
ValueSetter.call(authInput, token);
And now just trigger the "Input" event, and for some reason I need to set the bubbling of the event to True, so the event get dispatched on the ancestrals elements (maybe react listen to some wrapper/main element triggers and it's children just bubble the event so it can get to it, idk...):
var inputEvent = new Event("input", { bubbles: true });
authInput.dispatchEvent(inputEvent);
Related
I am creating a react js app and tried other available methods to fetch data from an API. I am looking for ways so that if there is an update in the API, react js automatically reflect an updated data. I have already tried component did mount and setting an interval, but looking for other ways.
Well for triggering side effects you can use the ComponentDidMount life cycle method if you are working with class components, or the useEffect hook if you are using functional components. Those are for when you need to trigger effects such as API calls when a component is rendered.
You can also trigger those effect using events based on client actions when interacting with your app, like press a button, a scroll event, a form submit, etc.
However the thing you are actually asking about reflect a change in the UI based on some API update is the use case for websockets. Check this link for getting a basic understanding of the use of websockets with React https://www.valentinog.com/blog/socket-react/
Also when you are ready check the official repository of SocketIO https://www.npmjs.com/package/react-socket-io
TL;DR:
I am rendering a BioDigital HumanAPI anatomical model in my Angular 5 app within an iFrame. I instantiate API object using:
this.human = new HumanAPI(iFrameSrc);
There's an API function human.on(...) that can be used to register click events from within iFrame (like picking objects from the model, etc.). I need this function to be able to listen to the events at all times. I do the object instantiation and put this function within ngOnInit() and it works, but when I change the source of iFrame to render a different model, this function stops working. Where should I put this listening function so that its logic is available at all times?
Longer version:
I am developing an Angular app using BioDigital HumanAPI. The basic idea here is that HumanAPI provides several anatomical models which can be rendered in a web-app using an iFrame (an example here). The src of this iFrame is a link, something like:
https://human.biodigital.com/widget?m=congestive_heart_failure
Since I want the user of my Angular app to be able to view several of such models, I have a list of these URLs, and based on user's selection, I update the src of iFrame, using a function updateFrameSrc which has following code:
iframeSrc: SafeUrl;
this.iframeSrc = this.sanitizer.bypassSecurityTrustResourceUrl(newUrl);
Finally (the question is coming, please stay with me), in order to manipulate and register different click events and user interactions with the model within the iFrame itself, we make a HumanAPI object like this:
this.human = new HumanAPI(iFrameID);
This lets us use API event listener functions like human.on('scene.picked') to register and save click events (like shown in the example I referenced above). All of this is working fine.
The problem is that since I initialize the human object in the ngOnInit() function and also put the human.on('scene.picked') function there, I cannot register the click events after the iFrame source is changed. As I understand it, ngOnInit() is only called once when the component is first initialized, so may be the listening logic of human.on is not available after updating the iFrame source? I have tried placing the logic in different life-cycle hooks but its doesn't work.
My current work-around is to re-call the ngOnInit() function after updating the iFrame source, and it works that way, but I believe this is against the standard life-cycle management practices.
My questions are:
It is acceptable to re-call the ngOnInit() function from within the component logic?
If not, where should I place a JavaScript API function that listens to click events from an iFrame at all times, even after the source of that iFrame has been changed?
As suggested in an earlier comment, you can just move the code in ngOnInit() to a separate function and call that function from both ngOnInit() as well as your update function.
Don't forget to re-initialize the human object of HumanAPI in that function as well, when updating the iFrame source.
Re-calling ngOnInit() should be avoided as it circumvents the acceptable functionality of lifecycle hooks, as mentioned by #iHazCode.
If you are looking for near real time you will want this to occur in the NgOnChanges life-cycle hook. Be advised this is expensive.
If slightly less "near real time" is acceptable, I would advise wiring up a rapid delay subject Observable.Interval(500) (also, but slightly less expensive) at the time of Component initialization NgOnInit.
Please DO NOT circumvent the hooks by re-calling ngOnInit.
If you have additional questions let me know.
I'm implementing a JavaScript-based Vaadin component that will need to show and update a relatively large data set. I'm doing this by extending AbstractJavaScriptComponent.
I'm trying to keep the JS side as "dumb" as possible, delegating user interactions to the server using RPC, and which updates the shared state. Then the JS connector wrapper's onStateChange function is called with the new state, which causes the DOM to be updated accordingly.
I have 2 problems:
I don't want to transfer the whole data set each time a small part gets updated.
I don't want to entirely rebuild the UI each time either.
I can solve the second problem by keeping the previous state and comparing parts of it to find out what changed and only make the necessary DOM changes.
But that still leaves the first problem.
Do I have to stop using Vaadin's shared state mechanism and instead only use RPC for communicating the changes to the state?
Update:
I've been doing some testing, and it certainly appears that Vaadin's shared state mechanism is horrible in terms of efficiency:
Whenever the component calls getState() in order to update some property in the state object (or even without updating anything), the whole state object is transferred. The only way to avoid this, as far as I can see, is to not use the shared state mechanism and instead use RPC calls to communicate specific state changes to client.
There are some issues with the RPC approach that will need to be resolved, for example: if you change a value multiple times within a single request/response cycle, you don't want to make the RPC call multiple times. Instead, you want only the last value to be sent just like the shared state mechanism only sends the final state in the response. You can keep dirty flags for each part of the state that you want to send separately (or just keep a copy of the previous state and compare), but then you need to somehow trigger the RPC call at the end of the request handling. How can this be done?
Any ideas on this are welcome!
Update 2:
Vaadin 8 fixes the root issue: it sends only the changed state properties. Also, it doesn't call onStateChange() on the JS connector anymore when only doing an RPC call (and not changing any state).
OP is correct in stating that shared state synchronisation is inefficient for AbstractJavaScriptComponent-based components. The entire state object is serialised and made available to the Javascript connector's onStateChange method whenever the connector is marked as dirty. Other non-javascript components handle state updates more intelligently by only sending changes. The exact place in the code where this happens is line 97 in com.vaadin.server.LegacyCommunicationManager.java
boolean supportsDiffState = !JavaScriptConnectorState.class
.isAssignableFrom(stateType);
I'm not sure why state update is handled differently for AbstractJavaScriptComponent-based components. Maybe it's to simplify the javascript connector and remove the need to assemble a complete state object from deltas. It would be great if this could be addressed in a future version.
As you suggest, you could dispense with JavaScriptComponentState completely and rely on server->client RPC for updates. Keep dirty flags in you server-side component or compare old state and new state by any mechanism you want.
To coalesce the changes and send only one RPC call for each change, you could override beforeClientResponse(boolean initial) in your server-side component. This is called just before sending a response to the client and is your chance to add a set of RPC calls to update the client-side component.
Alternatively, you could override encodeState where you have free-reign to send exactly whatever JSON you like to the client. You could choose to add a list of changes to the base JSON object returned by super.encodeSate. Your javascript connector could interpret as appropriate in its onStateChange method.
Edited to add: calling getState() in your server-side component will mark the connector as dirty. If you want to get state without marking it as dirty then use getState(false) instead.
Following our discussion about this, I've created a drop-in replacement for AbstractJavaScriptComponent that transmits state deltas and includes some extra enhancements. It's in the very early stages but should be useful.
https://github.com/emuanalytics/vaadin-enhancedjavascript
The solution is deceptively simple: basically re-enabling state difference calculation by bypassing this line of code in com.vaadin.server.LegacyCommunicationManager.java:
boolean supportsDiffState = !JavaScriptConnectorState.class
.isAssignableFrom(stateType);
The implementation of the solution is complicated by the fact that the Vaadin classes are not easily extended so I've had to copy and re-implement 6 classes.
Vaadin's shared state works exactly like you want out of the box: when a component is added to the DOM first time, the whole shared state is transferred from server to client, so that it's possible to display the component. After that, only changes are transferred. For example, one changes the caption of a visible component by calling component.setCaption("new caption"), Vaadin only transfers that new caption text to client and "merges" that to the client-side shared state instance of the component.
I would like to trigger an event on a meteor server when a document on my collection changes to a specific value, say some field changes from false to true.
I am familiar with binding events to the client; however, I want this event to only be called when the server state changes, specifically the value of a given document in my collection. I want to trigger an external HTTP call from the server when this happens, as I need to message external applications.
Seems like this is an old post. For the benefit of others.
Peerdb package seems to do some of the tasks you are looking for.
https://atmospherejs.com/peerlibrary/peerdb
Also a bit late, but the most classic solution to this type of problem is using the very popular meteor-collection-hooks library. In particular, you'd probably want to use the .after.update hook (click link for full documentation), which allows you to hook into a particular collection after an update is made to a document and compare before and after by comparing doc (doc after update) to this.previous (doc before update).
I am trying to create a web page with form that once user change any field, the validation and update commit immediately rather than letting user to click on submit button. I am using Knockout.js and mapping plugin. I know I can achieve this by creating a computed field for each original fields, but this kind of work is tedius and dumb, is there good practice to create a general listener to listen on any changes in any fields and update backend respectively?
In order to subscribe to any change you could use ko.toJS() method. Actually it allows to walk through object graph and unwrap observables. As your probably know when you use ko.computed it subscribes to all reads of observables fields and re-evaluate on every change. So if you use code like this:
ko.computed(function() {
ko.toJS(viewModel);
// update data on server
});
Also you should pay attention that this piece of code will update data on server right after initialization. It could be easily avoided. Please checkout this example: http://jsfiddle.net/UAxXa/embedded/result/
Also I think you might want to send only changed data to server. You could incorporate ko.editbales plugin ( https://github.com/romanych/ko.editables ) and some KO under-hood knowledge. Please checkout this sample: http://jsfiddle.net/romanych/RKn5k/
I hope it could help you.
You've got several options but if you want a single listener, one good way is to use the same code I used to create a change tracker. It simply listens for the observable changes. Its about 19 lines of code. You can grab this and instead of using it for change tracking, just wire in a method that saves the changes when they occur.
NuGet http://nuget.org/packages/Knockout.ChangeTracker
Codeplex http://kochangetracker.codeplex.com/
To Setup change tracking, add this tracker property to your view model:
viewModel.tracker = new ChangeTracker(viewModel);
Hook these into your view to determine when changes occur:
viewModel.tracker().somethingHasChanged();
Hook this into your view model when you want to reset state in functions (ex: load, save):
viewModel.tracker().markCurrentStateAsClean;
Optionally, you can pass your own hashFunction for state tracking, too.