How to trigger a button click event from the user's browser - sapui5

I'm trying to set up some UI automation in SAP Fiori by adding JavaScript to the page so that when a user loads a form, some fields are prefilled for them, and then it automatically goes to the next screen.
I can't make modifications in the SAP UI, this needs to be done in the browser at runtime.
I need to trigger the click of the 'Create' button above. The button's DOM id is __button9.
However, invoking the normal JavaScript click event doesn't work. I believe the SAP UI framework abstracts these events. So I'm wondering if there's a way to still trigger the underlying SAP event to invoke the click functionality?
Using the button.click function doesn't trigger the SAP button click event:
const button = document.getElementById('__button9');
button.click();
Dispatching the event like this also doesn't work:
const clickEvent = document.createEvent('MouseEvents');
clickEvent.initEvent('click', true, true);
const button = document.getElementById('__button9');
button.dispatchEvent(clickEvent);
This is a very product specific question, but a similar question has already been asked on the SAP support channels here and didn't receive a response.
This blog explains the SAP event model pretty well, but the use case of triggering button events from outside the context of the SAP UI Controller doesn't seem to be covered.

I found this in the SAP forum but haven't tried it yet.
sap.ui.getCore().byId( $('#navigation button').eq(0).attr("id")).firePress();
https://answers.sap.com/questions/10107636/unable-to-trigger-a-click-event-in-onafterrenderin.html
Maybe it helps.
Kind regards
Sebastian

https://sapui5.hana.ondemand.com/1.71.38/#/api/sap.m.Button/methods/firePress
Controls already have fireEvent method and derivatives in their API as fire+EventName.
var oButton = this.byId("button");
oButton.attachPress(function(oEvent){
console.log("Pressed!");
});
oButton.firePress();

Related

Fiori launchpad: handle logout event with custom backend call without `attachLogoutEvent` (UI5 < 1.81)

So, I found that the launchpad Container API provides an option to register a logout event with returning a promise (https://ui5.sap.com/#/api/sap.ushell.services.Container%23methods/attachLogoutEvent).
Unfortunately, after the implementation I found out that the UI5 version must be 1.81 or higher for parameter bAsync to work. In my project, we're at 1.78, so no promises for me.
What's the problem?
I want to make a backend call in the said logout event. This doesn't work, since, as far as I understood my debugging, the launchpad destroys everything just after my logout event has "finished" (= every line of code in the event has been gone through, ignoring sub-functions). Timeouts etc. don't work, because their calls would also be after code progressing has already finished, meaning the calls are deleted.
What have I tried?
Instant backend call without sub-functions → didn't work for the same reason as above.
Infinite while-looping until the backend call is processed → stack overflow.
While-looping with timeout/await → await not allowed in strict mode, timeout didn't work because of the above issue.
What do I think might work?
Stall code progression until the backend call has been finished.
Using a completely different method to get my logic into handling the logout (e.g. full custom logout).
Ask here for further ideas.
Does anyone have an idea on how to solve the issue with UI5 1.78?
Alright, I have found a solution to this. It's probably not the technically nicest, but it works and the result looks clean enough. This is from a S4/HANA system, so it might not be a universal solution (e.g. it doesn't consider logging off within the left-side pane which doesn't exist in my launchpad).
What did I do?
Instead of attaching my individual logic to the Fiori logout-event, I created a custom logout button with my individual logic, followed by calling the SICF logout node.
How did I do it?
Create a Launchpad plugin
In Component.js, add a new header item with custom logout function
// ushellLib required from "sap/ushell/library"
var oRenderer = ushellLib.Container.getRenderer("fiori2");
oRenderer.addHeaderEndItem("sap.ushell.ui.shell.ShellHeadItem", {
id: "logoutButton",
icon: "sap-icon://log",
// ...
press: [this._logout, this],
}, true, false);
_logout: function() {
this._callMyStuff();
window.location.href = "/sap/public/bc/icf/logoff";
},
In style.css, hide the original logout button (logoutBtn) in desktop (__list0...) and mobile (__list1...) to prevent skipping my logic by logging off via default logout.
#__list0-7-logoutBtn {
display: none;
}
#__list1-7-logoutBtn {
display: none;
}

Load select options dynamically from external service in Touch UI dialog in AEM?

My problem is to load a select field in touch UI dialog with dynamic options. These options are coming from a external URL via webservices, I am consuming this RESTful services using url defined in one of our global javascript objects like
$.get(mec.serviceConfig.baseUrl + '/movies';
Please understand that the options are coming from third party webservice please do not mention datasource. Whenever I search for loading select options dynamically I get this tutorial
Link to Tutorial
This is is not what I want.
In classic UI it is easy with optionsProvider.
In touch UI I am trying to write a script that fetches the data from the external webservice via AJAX ON DIALOG load and set these options in the select field.
Is there any better easier approach ? Can someone please share code snippets?
you should create JS listener for your component.
$document.on("dialog-ready", function() {
// there you should find your select field
//for example
var language = $("[name='./language']").closest(".coral-Select");
//then append to your select field new options from your datasource
});
please see doc: Dynamically updating AEM TouchUI Dialog Select Fields

How to integrate Google Identity Toolkit with a single webpage app (e.g. GWT)

I need to integrate Google's identitytoolkit (Google's identitytoolkit) with my Google webtoolkit (GWT) application.
However rendering the gitkit signInButton or widget is already not straightforward because the way to do it is linked to "window.onload". And I need to render the widget at a later moment.
I managed to do it (see below), but I am not happy with this and I wonder if someone else found a better way of integration.
private native void showGitKitWidget() /*-{
$wnd.google.identitytoolkit.signInButton(
'#gitkitDivId', // accepts any CSS selector
{
widgetUrl: "//127.0.0.1:8888/gitkit/signin.html",
signOutUrl: "/gitkit/signout",
popupMode: true
}
);
var evt = $doc.createEvent('Event');
evt.initEvent('load', false, false);
$wnd.dispatchEvent(evt);
}-*/;
Update
Actually using the popupMode parameter for the signInButton makes a seamless integration even closer. This lets the widget popup in a browser window and leaving the GWT window unchanged. Then in the widget instead of redirecting to the successUrl I use the JS callback to trigger an AJAX call instead.
var config = {
idps: ["googleplus"],
signInSuccessUrl: '//127.0.0.1:8888/gwt/servlet/gitkit/signedin',
callbacks: {
signInSuccess: function(tokenString, accountInfo,
opt_signInSuccessUrl) {
/* !!! Tell GWT parent window that we are ready...
I believe using a cookie for which the parent is
regularly looking is the way to go, because it
will work in mobile browsers too.
*/
return false; // prevents redirect to signInSuccessUrl
}
}
Update Finally the GWT parent window will wait for the result cookie and if found make the AJAX call to the signInSuccessUrl. It will also have to render the signInButton again, which will then show the signed in user.
So the only ugly workaround is how the signInButton is rendered using the onload method call.
It would be very helpful if there would be a way to render the signInButton dynamically when needed, for instance if there were a "$wnd.google.identitytoolkit.update()" method. This could be called any time for the first time and should also be able to handle signin-status change!
In conclusion, I have answered my own question, which might be helpful to others, but also I would still like to ask if there would be a better way, which I missed.
As you've noted, the Identity Toolkit widget currently needs to be triggered by page load. Single-page applications (like those built with GWT) should place the widget on a separate page. Then you can redirect the user there - or render via popup, as you've noted - to sign in the user in.
If rendering the sign-in button is a problem, there is documentation on how to load the widget directly.

How do I correctly Implement an event on successful form Submission using Google Tag Manager and Sitecore's Web Forms for Marketers?

I am attempting to track successful form Submissions using an event in Google Analytics via Google Tag Manager. My current setup successfully tracks when users submit the form. However, the event still fires even when the form submission is invalid and does not submit (ie a user hasn't filled out all of the required fields, clicks the submit button, the form attempts to validate, but comes back to the user with errors instead of submitting). I have the Check Validation feature on my listener checked which theoretically should keep the tag from firing if the form submission is prevented, so it's not the obvious error.
The form in question is created with Sitecore's Web Forms for Marketers. Colleagues of mine have had similar unsolved issues with their WFFM forms.
This particular form is used to gate content so that only users who fill out the form will have access to the content resource. So for example if I go to www.mydomain.com/resource I will be redirected to www.mydomain.com/form where if I fill out all of my information correctly and submit it I will then be redirected to the resource that I was originally attempting to view at www.mydomain.com/resource.
Here's my setup:
Tag 1
Name: Form Submission Listener
Type: Form Submit Listener
Wait For Tags: Checked
Max Wait Time: 2000 milliseconds
Check Validation: Checked
No advanced Settings
Firing Rule: On form pages by URL
Tag 2
Name: Event Form Submission
Type: Universal Analytics
Tracking ID: UA-.....
Enable Display Advertising Features: Checked
Track Type: Event
Category: Form
Action: Submission
Label: {{Form resource URL}}
Non-Interaction Hit: False
No More Settings
No Advanced Settings
Firing Rules: {{event}} equals gtm.formSubmit
Theoretically the Check Validation check box should prevent the tag from firing if the form does not successfully submit, but in the case of this form it does not. The tag fires regardless of whether the form submits or not.
Apologies that I cannot link to the form as it is for a client and behind security.
We were able to find an answer to our question via the Sitecore forums, but I wanted to pass it along for your benefit.
From Sitecore:
The Web Forms module provides the double level validation, 1-client validation, 2-server validation.
By default, the client validation is disable for the Required Field validator. So, when you press Submit, the form posts to the server, and returns with the validation error. It's a possible reason why Google Analytics considers that as a form submit.
Find the following item in the Master database:
/sitecore/system/Modules/Web Forms for Marketers/Settings/System/System Validation/NotEmpty
Find the "Enable Client Script" checkbox and enable it.
Save and publish the item.
Check whether the issue was fixed.
This fixed the issue for all of our text based fields. It did not fix the issue for the one checkbox on the form. I've followed up with sitecore on this, but I figured that I'd update here in the meantime.
With only the checkbox remaining I was also able to use a a macro and add to my original firing rule in google tag manager so that the event would not fire if the checkbox was not checked.
I created a Custom Javascript Macro called Radio Button Checked (not sure it's the best, but it worked), and added a new condition to my original Form Submission Rule: {{Radio Button Checked}} equals true
The macro:
function() {
var radioName = "radioButtonName";
try {
var buttons = document.getElementsByName(radioName);
for (var i = 0;i < buttons.length;i++){
if(buttons[i].checked) {
return true;
}
}
} catch(e) {}
return false;
}
EDIT: Sitecore got back to me about the checkbox issue.
From Sitecore:
Currently the CheckBox field type doesn't have the client-side validation. I registered it as a bug for the WFFM module. I'll let you know as soon as it's fixed.
They let me know also that this isn't something that will be fixed near-term so I need to continue using my GTM workaround for the check box field.
The Google Chrome plugin "Tag Assistant" is super helpful in debugging these sorts of issues. It will show you what (if any) structural or implementation issues exist on a given page that might be preventing your intended tracking behavior (https://chrome.google.com/webstore/detail/tag-assistant-by-google/kejbdjndbnbjgmefkgdddjlbokphdefk?hl=en)
My gut feel is that this issue is not specifically related to WFFM, but may be due to the implementation of the Tag Manager code on the page. I seem to recall having an issue like this when the Tag Manager include code gets dumped inside the auto-generated .NET tag when using WebForms in general. Google's docs (https://developers.google.com/tag-manager/quickstart) say to put it immediately after the opening tag, and I recall that being my issue with tracking form submits.
This is all from memory, so I could be wrong, but it's something else to check.
Good luck!

Better way to temporarily disable ajax submission on ajaxForm plugin

I'm using the jQuery malsup ajaxForm plugin on a form. I've got a bunch of POST vars that get submitted and this is working fine, I want to use the same post vars to perform an Export to file option. This means using the same form for both submission types.
Because you can't download Files through an AJAX submission, I'm using .unbind('submit').submit() on the form to prevent the previously assigned ajax event handlers from firing.
After this unbinding occurs, I then have to re-run the ajaxForm constructor when the user wants to change the filters using AJAX (not for the export).
Before I invest more time in fixing the edge cases and a couple of bugs, I wondered if there was a cleaner way to do this?
Use custom events and trigger()!
First, put a radio button on your form to allow the user to switch between AJAX/Export to file. Let's say the name of this field is submitAction
Second, your submit listener acts only to decide what happens next based upon the value of the submitAction radio. This is where you fire the custom events (we define them in step 3):
$('form.specialform').on('submit',function(e){
e.preventDefault();
var checked = $(this).closest('[name="submitAction"]').filter(':checked');
if(checked.val() == 'ajax'){ //ajax!
$(this).trigger('submitAJAX');
} else { //export to file!
$(this).trigger('submitExport');
}
});
Third, define your custom events with two event listeners:
$('form.specialform').on('submitAJAX',function(e){
//do AJAX call here
});
$('form.specialform').on('submitExport',function(e){
//do file export here
});
As you can see, doing it this way allows you to avoid the mess of unbinding and rebinding the same event handlers a bunch of times.
Does that help?
Thanks a lot to Jonathan for the above answer. We too were faced with similar problem and triggering custom events did the trick for us.
Also i would add that if malsup ajaxForm plugin is being used, we should invoke 'ajaxSubmit' instead of ajaxForm as ajaxForm doesn't submit the form.
$('form.specialForm').on('submitAJAX',function(e) {
$(this).ajaxSubmit({
target: '#query_output',
beforeSubmit: showLoading,
success: hideLoading
});
});