Frame contents loaded event in GWT - gwt

I am creating a DialogBox that contains a Frame widget. I have tried using both the addLoadHander and addAttachHandler hooks to try and capture changes to the frame with no success. Only the initial page load fires either event and after some research it appears that both actually occur when the Widget is attached to the DOM and not when the frames content is loaded.
DialogBox paymentProcessingDialog = new DialogBox(false);
private void submitPayment(String actionURL, String qryString){
Frame processCCWindow = new Frame(actionURL + "?" + qryString);
processCCWindow.addLoadHandler(new LoadHandler() {
#Override
public void onLoad(LoadEvent event) {
//This is called when the frame is attached to the dom
//and not when the document is actually ready so it's
//kinda worthless for my scenario
//Note: the addAttachHandler has the same issue
//***call to JSNI idea (see below)
checkURLChange();
}
});
//...irrelevant code to style the DialogBox
VerticalPanel panel = new VerticalPanel();
panel.add(processCCWindow);
paymentProcessingDialog.setWidget(panel);
paymentProcessingDialog.center();
paymentProcessingDialog.show();
}
The URL that is loaded into the frame contains an HTML response from an external server (Paypal - transparent redirect) that immediately re-submits itself back to my server via a form. I am trying to capture the body of the frame once the submission back to my server has completed and the document in the frame has loaded.
I have also tried to use a JSNI method that uses a MutationObserver (got the code from this solution) to try and watch the frame but I believe the JSNI is actually being run inside the parent and not inside the Frame(?) so nothing happens.
I have also tried using a setTimeout to trigger another method in my presenter class but I believe that is running into the same problem as the MutationObserver idea because the console statements never fire.
private static native void checkURLChange()/*-{
new MutationObserver(function(mutations) {
mutations.some(function(mutation) {
if (mutation.type === 'attributes' && mutation.attributeName === 'src') {
console.log(mutation);
console.log('Old src: ', mutation.oldValue);
console.log('New src: ', mutation.target.src);
return true;
}
return false;
});
}).observe(location.href, {
attributes: true,
attributeFilter: ['src'],
attributeOldValue: true,
characterData: false,
characterDataOldValue: false,
childList: false,
subtree: true
});
window.location = "foo";
setTimeout(function() {
#com.myPackage.MyoutPresenter::paymentProcessComplete(Ljava/lang/String;)(document.getElementsByTagName("body")[0].innerText);
console.log("test" + document.getElementsByTagName("body")[0].innerText);
}, 3000);
}-*/;
Does anyone have any thoughts on how I can accomplish this?

I am not familiar with Frame widget, but it appears that you add a handler after you set the URL. Try to build the Frame first (outside of submitPayment, if you use it more than once), and then set the URL:
final Frame processCCWindow = new Frame();
processCCWindow.addLoadHandler(new LoadHandler() {
#Override
public void onLoad(LoadEvent event) {
// do something
}
});
private void submitPayment(String actionURL, String qryString){
processCCWindow.setUrl(actionURL + "?" + qryString);
}

Related

AEM Workflow custom input data

I need to create a workflow in AEM that for a page (specified as payload) finds all the assets used on the page and uploads a list of them to an external service. So far I have most of the code ready, but business process requires me to use a special code for each of the pages (different for each run of the workflow), so that the list is uploaded to correct place.
That is when I have a question - Can you somehow add more input values for an AEM workflow? Maybe by extending the starting dialog, or adding some special step that takes user input? I need to be able to somehow specify the code when launching the workflow or during its runtime.
I have read a lot of documentation but as this is my first time using workflows, I might be missing something really obvious. I will be grateful for any piece of advice, including a link to a relevant piece of docs.
Yes, that is possible. You need to implement a dialog step in your workflow: https://docs.adobe.com/content/help/en/experience-manager-64/developing/extending-aem/extending-workflows/workflows-step-ref.html#dialog-participant-step
You could:
Create a custom menu entry somewhere in AEM (e.g. Page Editor, /apps/wcm/core/content/editor/_jcr_content/content/items/content/header/items/headerbar/items/pageinfopopover/items/list/items/<my-action>, see under libs for examples)
Create a client-library with the categories="[cq.authoring.editor]". So it is loaded as part of the page editor (and not inside the iframe with your page)
Create a JS-Listener, that opens a dialog if the menu-entry was clicked (see code). You can either use plain Coral UI dialogs, or my example misused a Granite page dialog (Granite reads the data-structure in cq:dialog, and creates a Coral UI component edit-dialog out of it - while Coral is the plain JS UI-framework)
Create a Java-Servlet, that catches your request, and creates the workflow. You could theoretically use the AEM servlet. But I often have to write my own, because it lacks some features.
Here is the JS Listener:
/*global Granite,jQuery,document,window */
(function ($, ns, channel, window) {
"use strict";
var START_WORKFLOW_ACTIVATOR_SELECTOR = ".js-editor-myexample-activator";
function onSuccess() {
ns.ui.helpers.notify({
heading: "Example Workflow",
content: "successfully started",
type: ns.ui.helpers.NOTIFICATION_TYPES.SUCCESS
});
}
function onSubmitFail(event, jqXHR) {
var errorMsg = Granite.I18n.getVar($(jqXHR.responseText).find("#Message").html());
ns.ui.helpers.notify({
heading: "Example Workflow",
content: errorMsg,
type: ns.ui.helpers.NOTIFICATION_TYPES.ERROR
});
}
function onReady() {
// add selector for special servlet to form action-url
var $form = ns.DialogFrame.currentFloatingDialog.find("form");
var action = $form.attr("action");
if (action) {
$form.attr("action", action + ".myexample-selector.html");
}
// register dialog-fail event, to show a relevant error message
$(document).on("dialog-fail", onSubmitFail);
// init your dialog here ...
}
function onClose() {
$(document).off("dialog-fail", onSubmitFail);
}
// Listen for the tap on the 'myexample' activator
channel.on("click", START_WORKFLOW_ACTIVATOR_SELECTOR, function () {
var activator = $(this);
// this is a dirty trick, to use a Granite dialog directly (point to data-structure like in cq:dialog)
var dialogUrl = Granite.HTTP.externalize("/apps/...." + Granite.author.ContentFrame.getContentPath());
var dlg = new ns.ui.Dialog({
getConfig: function () {
return {
src: dialogUrl,
loadingMode: "auto",
layout: "auto"
}
},
getRequestData: function () {
return {};
},
"onSuccess": onSuccess,
"onReady": onReady,
"onClose": onClose
});
ns.DialogFrame.openDialog(dlg);
});
}(jQuery, Granite.author, jQuery(document), window));
And here is the servlet
#Component(service = Servlet.class,
property = {
SLING_SERVLET_RESOURCE_TYPES + "=cq:Page",
SLING_SERVLET_SELECTORS + "=myexample-selector",
SLING_SERVLET_METHODS + "=POST",
SLING_SERVLET_EXTENSIONS + "=html"
})
public class RequestExampleWorkflowServlet extends SlingAllMethodsServlet {
#Override
protected void doPost(#Nonnull SlingHttpServletRequest request, #Nonnull SlingHttpServletResponse response) throws IOException {
final Page page = request.getResource().adaptTo(Page.class);
if (page != null) {
Map<String, Object> wfMetaData = new HashMap<>();
wfMetaData.put("workflowTitle", "Request Translation for " + page.getTitle());
wfMetaData.put("something", "Hello World");
try {
WorkflowSession wfSession = request.getResourceResolver().adaptTo(WorkflowSession.class);
if (wfSession != null) {
WorkflowModel wfModel = wfSession.getModel("/var/workflow/models/example-workflow");
WorkflowData wfData = wfSession.newWorkflowData(PayloadInfo.PAYLOAD_TYPE.JCR_PATH.name(), page.getPath());
wfSession.startWorkflow(wfModel, wfData, wfMetaData);
MyServletUtil.respondSlingStyleHtml(response, HttpServletResponse.SC_OK, "Triggered Example Workflow");
} else {
throw new WorkflowException("Cannot retrieve WorkflowSession");
}
} catch (WorkflowException e) {
MyServletUtil.respondSlingStyleHtml(response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.getMessage());
}
} else {
MyServletUtil.respondSlingStyleHtml(response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Internal error - cannot get page");
}
}
}

Lazily Initialize/Load a GXT Widget When Its Parent Becomes Visible

I am very new to GXT (and GWT/GWTP for that matter). I want to know if I can lazily load a GXT (4.0) [custom] widget when a modal Dialog that contains its panel is displayed (dialog initially not shown and only appears when a button is clicked).
The reason I want this behavior is that this widget needs to load an applet HTML code which is not available when the page initially instantiates all the other field widgets/panels/dialogs. Therefore, I need to delay obtaining the applet code from another application until the dialog is explicitly appears.
Is this possible?
Lazy loading of images and urls loaded into a GWT Frame (IFrameElement). You can catch the event once say the image/url is loaded using the LoadHandler event.
private void loadLoginPage() {
final Frame frame = new Frame(url_base + url_login); // set the frames url
frame.addLoadHandler(new LoadHandler() {
#Override
public void onLoad(LoadEvent event) {
// Get the document from the loaded frame (similar to RootPanel on main page)
Document doc = IFrameElement.as(frame.getElement()).getContentDocument();
// From here you can wrap widgets in the frame like this
final TextBox username = TextBox.wrap(doc.getElementById("username")); // from the html doc using id="username"
// grab a div element
DivElement div = DivElement.as(doc.getElementById("mydiv"));
// Create content to be added to the doc
ButtonElement button_elem = doc.createPushButtonElement();
Button button = Button.wrap(button_elem);
// attach to the document
div.appendChild(button.getElement());
}
});
// Attach to DOM
RootPanel.get("mything").add(frame);
}
And similar for image loading.
Image image = new Image(image_url);
image.addLoadHandler(new LoadHandler() {
#Override
public void onLoad(LoadEvent event) {
// image is ready to be used.
}
});
// attach to DOM to initiate loading of the image
Hope this helps...

Layout update GWT

I use RPC calls to connect to mySql and bring text data from there.
My page is defined as split Layout.
my problem is that I don't know how to update the main layout with different text.
if i use the clear() method it will remove all the layout !
"p" is the splitLayout.
RPC:
rpcService.getChapterTxt(selectedBook,bookChapters[selectedBook],
new AsyncCallback<List<BibleTxt>>(){
public void onFailure(Throwable caught)
{
Window.alert("Failed getting Chapter");
}
public void onSuccess(List<BibleTxt> result)
{
int i = 0 ;
String verseText ="";
//Label verseLabel = new Label();
PPanel chapterPar = new PPanel();
HTML page= new HTML(verseText);
for(i=0;i<result.size();i++)
{
verseText = result.get(i).getVerseText();
//verseLabel.setText(verseText);
page.setText(page.getText() + verseText);
}
chapterPar.add(page);
//p.clear();
p.add(chapterPar); // adds the main layout
}
});
Why you don't reuse the text component changing its content text instead of continuously detaching/attaching elements to the widget hierarchy. That way should perform better and cause less problems.

How do I clear a wicket AutoCompleteTextField after a choice is selected?

I'm using an AjaxFormComponentUpdatingBehavior to do some stuff when a choice is selected from an AutoCompleteTextField. After that stuff is done I want to clear the field but it's not behaving the way I expected it to.
Here're the relevant bits of code:
final AutoCompleteTextField<String> searchField =
new AutoCompleteTextField<String>(id, model);
searchField.add(new AjaxFormComponentUpdatingBehavior("onchange")
{
#Override
protected void onUpdate(AjaxRequestTarget target)
{
// Do stuff with the selected value here
...
searchField.clearInput();
target.addComponent(searchField);
}
});
I'm putting the value in a ListView and adding that to the target also. It gets updated correctly but the AutoCompleteTextField doesn't.
I think your example doesn't work, because you rerender the component on the client side using the model on the server side. If you reset the model value and repaint the component it has to work.
searchField.setModelObject(null);
target.addComponent(searchField);
However it is not neccessary to render the whole component, just clear the value on server and client side is enough.
The following example clears the model object and reset the field value by javascript (jQuery).
final AutoCompleteTextField<String> searchField =
new AutoCompleteTextField<String>(id, model);
searchField.setOutputMarkupId(true);
searchField.add(new AjaxFormComponentUpdatingBehavior("onchange")
{
#Override
protected void onUpdate(AjaxRequestTarget target)
{
// Do stuff with the selected value here
...
searchField.clearInput();
searchField.setModelObject(null);
target.appendJavascript("$('#" + searchField.getMarkupId() + "').val('');");
}
});
If you are using Wicket prior 6.x then the jQuery is not included. You can use the following JS:
target.appendJavascript("document.getElementById('" + searchField.getMarkupId() + "').value = '';");

GWT MAP 3.8 (Google API) - Is onResize() needed to hide the "grey background"?

My problem:
- I display a map inside a popup and I have unloaded tiles (grey background).
- If I zoom out or in, then the map will fill the entire space (no grey background anymore).
My question:
- Have you any idea about my problem (Should I need to resize to hide the "grey background") ?
- I do not know if I should call onResize() inside the Runnable callback (code is above) or not ?
Thanks you,
My actual code: (I am using the javaxLoaderAPI)
// ENTRY POINT
GoogleMap map;
#UiField LayoutPanel gmap;
public void AjaxLoader_MAP() {
AjaxLoaderOptions options = AjaxLoaderOptions.newInstance();
options.setOtherParms("key=***&sensor=false&language=es");
Runnable callback = new Runnable() {
public void run() {
gmap.onResize(); // Should I call onResize() here ?
map = GoogleMap.create(gmap.getElement());
};
}
AjaxLoader.loadApi("maps", "3", callback, options);
}
May be related to the following post - GWT Google Map Api V3 - broken when changing it
I just posted an answer to that unanswered post as well. I believe it answers and sheds insight on this one as well.
I know this is an old post, but let me know if helps!
When calling mapWidget.triggerResize(), it's important to call it with some delay (Timer.schedule()) so that all the widget have been reset and then the map is resized.
This is how I trigger it:
#Override
protected void onReset() {
super.onReset();
Timer timer = new Timer() {
#Override
public void run() {
triggerResize();
}
};
timer.schedule(100);
}
Without timer, I was still getting grey tiles. Hope someone still finds it useful.