My question is very basic on one hand but on the other hand the general situation is more complex, plus I cannot really get any working sample.
I'm developing/maintaing a web-application which is currently in transition from GWT code base into Ember.js.
Most of the newer code already relies on Ember.js and I think it's really awesome.
The problem is we cannot use Ember Router as all the request are being handled by the GWT.
In order to enabled the application run in this unusual configuration we have special JavaScript files that create our Ember main objects (Controllers & Models) for us.
As you can imagine navigation between tabs is cumbersome and is handled by GWT who creates Ember objects when needed. We are in transit toward a brave new world Ember Router and all.
But in the meantime, this is the problem I'm facing right now.
The user clicks a link which opens a page that contains some Ember based table.
The data is retrieved form the server using some Ajax code. Upon success it spawns a forEach loop which tries to pushObject all the received date into our Ember based components.
My problem happens when the user quickly switches between tabs. In this case the first list of object has not finished rendering yet and suddenly there's a new set of objects to handle. This causes Ember to throw errors like:
"Uncaught Error: Cannot perform operations on a Metamorph that is not in the DOM. "
and
"Uncaught NotFoundError: An attempt was made to reference a Node in a context where it does not exist."
Is it possible to prevent the loop from trying to render?
I've tried checking if the controller in question is already inDOM and it is, is there a way to notify Ember this object is no longer valid?
Sorry for a lengthy question and lack of running sample.
I'd probably modify the switch tab code to only execute afterRender has completed, that way you aren't mucking with ember objects while they are being used.
Ember.run.scheduleOnce('afterRender', this, function(){
// call GWT switch tab routine
});
Thank you Daniel and Márcio Rodrigues Correa Júnior. eventually what I did is to add a patch that would check the current context of the application (in my case the currently selected tab). If upon receiving the AJAX response the application is in the correct context (meaning the user haven't change the tab) go on. Otherwise just ignore the response and do not try to render it.
Now it seems to be working
Related
I have a panel used in multiple pages in an app we're developing. In this panel is a FileUploadField that uses AjaxFormSubmitBehavior (extended as FileUploadBehavior) on "change" to upload a file, which I then add to a list via ajax, update the view, clear the FileUploadField, and then allow them to select another file. And this is in fact exactly what happens in one of the pages using the panel...but not in another. In the non-working page, the first file selected is repeated over and over regardless of what file is picked after the first.
In the onSubmit of the behavior, we get the the FileUpload object which is supposed to be different between requests. I can see in the debugger that the FileUpload is the exact same object as the previous request, not merely carrying the same payload.
I'm scrutinizing the usage of the panel on the two pages and see no material differences. I can see the file control on the page DOES show the changed file name while I sit at my breakpoint on the server (so I suspect whatever is going wrong is on the java side). But I can't figure out why they behave differently or see where it's going wrong. The panels and pages are large and complex, so here's snippets of the relevant pieces.
FileUploadBehavior.onSubmit(AjaxRequestTarget) :
FileUploadField fileUploadField = (FileUploadField) fileUploadContainer.get("fileUploadField");
FileUpload fileUpload = fileUploadField.getFileUpload();
[...]
//clear file input after each request for next upload.
fileUploadField.clearInput();
target.add(fileUploadField);
I have a break right after this line and can see the first file gets repeated. The code that instantiates the field and behavior in the panel looks like:
FileUploadField fileUploadField = new FileUploadField("fileUploadField");
fileUploadField.add(new FileUploadBehavior("change", maxFileSize).setDefaultProcessing(false));
fileUploadContainer.add(fileUploadField);
The html tag:
<input wicket:id="fileUploadField" class="form-control" type="file" id="formFile" multiple>
I feel like the fact that it works in one page and not in another leads me to think the problem is outside the panel. The fact that the control in the browser shows the 2nd filename during test leads me to think it's on the java side. But nothing about the file event or definition happens outside the panel itself. The form elements are declared identically, and both have multipart enctypes when the pages render. Both successfully upload their first file. I'm kind of not even sure where to look for why wicket is re-using the FileUpload object in one page but not in another.
I should mention that we use Apache Wicket 6.26.
update: I looked into the source of FileUploadField, and it has an explicit check on whether FileUploads is null in it's internal property, and if so returns it without checking the actual request. I don't see any way to clear this value between requests. clearInput() doesn't affect it from what I see. I'm more confused by how this is working in one page than why it's not in the one where it doesn't now. I also don't know how to make the class 'reset' between requests.
Okay, figured this out. As martin-g pointed out, the fileUploads is set null in onDetach(), which I discovered about an hour after my update. The problem is that the onDetach() first tries to null out the model object. But that method was blowing up because there was no method 'fileUploadField' on the model attached to the form which was a compound property model. The page that worked does NOT use a compound property model for the form. For some reason, when this error occurred, it was being swallowed somewhere in the call stack and did not end up in my console log.
My solution was to provide a local model to the fileUploadField since that's not how I'm interacting with the control anyway (I'm using ajax and getting the FileUpload directly each time). That fixed it. It now works everywhere.
I use SAP standard library: Inbox.
in library class S3.controller by tap on attachments icon is onTabSelect event executed, witch makes
this.fnDelegateAttachmentsCreation();
this.fnFetchDataOnTabSelect("Attachments");
this.fnHandleAttachmentsCountText("Attachments");
this.fnHandleNoTextCreation("Attachments");
break;
fnFetchDataOnTabSelect makes an asynchronous call. During this call is fnHandleAttachmentsCountText already executed, so the update of attachments count occurs before the request for attachments is ready. As far the request for attachments is ready, there is no update for title executed.
On screenshot is AttachmentCountText „Attachnents (1/1)“, that comes from previously selected item.
It should be „Attachements (2/2)".
Also if response comes too quick, then view changes to loading view after it received the answer from request.
If list of attachments was updated from request callback, then it should not be updated second time.
Here it seems, that there is something on loading, but request is already finished.
How could be Inbox extended, to update the attachment header and content after request is ready?
used SAPUI5-Version: 1.71.4
You are using the standard bsp-application ca_fiori_inbox?
You didn't create an extension project of the application ca_fiori_inbox with custom coding?
If that's the case, it's a bug in an standard application delivered by the SAP. SAP releases so called notes to fix bugs in their standard applications.
You can import notes in your system via the transaction SNOTE. May ask a SAP Basis Administrator from your company for help.
The following notes exactly describe your problem
2873960
2823664
2916255
2901520
If you already extended the SAP standard application(MyInbox) without using ExtensionPoints codechanges in the standard application will not affect your custom extension.
When overriding a controller method, any functionality that was previously provided by it is no longer available. Likewise, any future changes made to the original controller method implementation will not be reflected in the custom controller.
In this case you could still implement the note and check the changes in the standard controller vs. your custom controller on your system and change the respective lines in your custom coding.
Don't fix SAP coding. Report it and get a fix.
The Note 2873960 corrects coding in an abap class, not in the bsp-application(ca_fiori_inbox). So definitly import the note and check if it's fixing your problem.
I actually do not know, how the app works, but I want to give it a try.
Since it is an asynchronous function, you can always also wait for the function until it is done. So in your case, you could try to set an await keyword in front of the function.
await fnFetchDataOnTabSelect("Attachments");
This will now wait on this position until it has finished the function call before it will call the next functions. In addition to that you also need to set the upper function onTabSelect to async. So in the end it should look something like this.
onTabSelect: async function() {
// ...
this.fnDelegateAttachmentsCreation();
await this.fnFetchDataOnTabSelect("Attachments");
this.fnHandleAttachmentsCountText("Attachments");
this.fnHandleNoTextCreation("Attachments");
// ...
}
Although the Web IDE maybe shows you errors, it does work, since it is an official JavaScript API.
Has anyone successfully based an application off the WebIDE Fiori CRUD template? It doesn't seem to be able to handle errors in the batch OData request properly.
For example when you select an item in the master list and click EDIT to update it and I enter a value that I know will cause an error in the backend. The batch OData request is sent and the response is successful (overall) even though internally the update failed. You see a bad request 400 error in the console but the app doesn't react to it.
Using the ErrorHandler.js object you can easily code a message box to show a nice error message, however the app seems to get confused and always navigates to the top item in the list but the ODataModel still has pending changes so if you try to navigate it shows the data loss dialog.
Trying to debug it is very painful given the nature of the code with Promises inside promises inside promises and callbacks firing everywhere!!
Maybe there is a better example CRUD application to base your apps on?
Well.... Some more debugging and I've found that all of the weird issues occurring with the Fiori CRUD template are due to the model defaulting to auto refreshing after changes.
Which typically is a nice feature as the user does not have to hit the refresh button to see changes that have been made after adding a new entry or editing an object.
But this auto refresh on the model seems to play havoc with updates in batch mode.
Firstly, if the batch update is completely successful the app navigates to the first item in the master list - which is should not.
Secondly, when there is an error in the batch of sorts of weird shit happens.
So to fix this I just call oModel.setRefreshAfterChange(false) before the oModel.submitChanges() call (only when updating, not creating). The in the callback after the save is completed we reset auto refresh to true.
The crud app now works as expected and can correctly handle errors in the odata batch update without strange ui updates.
Jason,
but the error message they are providing is very simple, and does not support parsing error body like :
<code>005056A509B11ED199D882461C8C40FE</code><message xml:lang="en">The Data Services Request version '3.0' is not supported for the request payload.</message><
we have to do the code for parsing the errors then show in the message box
I have a Meteor application using angular-meteor. I need now to load different angular modules depending on url. I added iron-router to my application to do so and I continue to handle routes for each module using ngRoute and anchor nav but it behaves strangely if url contains params. I made a small test case which is available here:
https://github.com/clouchtibat/iron-router-ng-route
If you click on 'truc' link and then on 'test', next routes changes will make controller be instantiated two times. It works if urls have no params.
I also tested with ui-router (in the with-ui-router branch) and the problem is the same but in addition view is duplicated.
Is this a bug in one of the two routers or is there something wrong with my implementation?
Take a look at this conversations in the angular-meteor Github issues:
https://github.com/Urigo/angular-meteor/issues/154
https://github.com/Urigo/angular-meteor/issues/493
I think it can help you with some directions.
I am also having some hard time with mixin angular-meteor and iron:router.
In our app we need to check if the data is saved when we are in a particular place before navigating away from it. So the user should be able to negate a browser back button request. But by the time that the history value change event is received the url has already been changed. The History class doesn't seem to have a way to restore the url back. Anybody have any ideas?
In GWT 2.1 you get Activities and Places. And activity has a maystop method, which is exactly what you want, if I understand you correctly.
Use a window.onunload or window.onbeforeunload javascript callback to confrim/save state.
onbeforeunload example
I haven't actually implemented this behavior yet, but here is my plan and maybe it will work for you.
1) Each time you receive an onHistoryChanged event and decide to allow it, save the current historyToken in an instance variable somewhere.
2) Keep track of activity on the page that should block navigation. Use a data structure that can keep track of multiple activities, like multiple file uploads, multiple edits, etc.
3) When you receive a new onHistoryChanged event, if your data structure from #2 indicates that it's not safe to navigate, avoid changing the page and restore the historyToken that you saved in #1. I'm assuming that you can do this either by:
a) Calling History.newItem(oldHistoryToken, false) or
b) Calling History.newItem(oldHistoryToken, true) and keeping a flag to force the next onHistoryChanged to be ignored.
Again, I haven't actually implemented this so let me know how it works out.
If you have links that allow the user to leave the app and you want to prevent that as well, you'll need to also add an onbeforeunload.
Have a look at the PlaceManagerImpl class from the gwt-platform framework. Especially the onValueChange() method and the methods dealing with the onLeaveQuestion field.
Hope that helps.
In this issue report, t.broyer explains in his comment that such behavior was planned during design of Places framework. The most important part is:
mayStop was a mistake, or it should have only been called when unloading the app, not for internal navigation within the app.
So probably it's better to not use it at all...