I want to perform some operation when my application gets killed. Which method can be used for this? I am working on React Js.
Preventing the whole browser from being killed isn't possible, as that behaviour could easily be exploited by malicious scripts. You can, however, detect when the tab is closed.
Try the window: beforeunload event
window.addEventListener('beforeunload', function (e) {
// Cancel the event
e.preventDefault();
// Chrome requires returnValue to be set
e.returnValue = '';
});
Related
As I have understood, Socket.io automatically disconnects on page refresh. However, when using react-router-dom, a disconnect message is not sent to the backend, how would one implement this? I don't think code is needed, however feel free to ask!
Try to handle the disconnection on window unload event in this way:
window.addEventListener('beforeunload', function (e) {
e.preventDefault();
e.returnValue = ' ';
});
window.addEventListener('unload', function (e) {
socket.emit('disconnection message', {data to be sent});
});
In that case you will have to handle it manually in your component using socket.disconnect(). react-router provides a SPA functionality and in that context, the page doesn't actually reload, otherwise you lose the current state and everything starts from scratch.
I am using Electron do make a desktop version on my Ionic React app. I've decided that the splash screen is only going to disappear when the React app says it can.
In index.ts (the main Electron file), I create an event listener like this:
ev = splashScreen.on(splashScreen.events.APP_READY_TO_START, () => {
splashScreen.off(ev, true);
splashScreen.destroy();
mainWindow.show();
}, true);
This listens for the OL-APP_START_SPLASH_CLOSE event through ipcMain. The on function looks like this:
on(event: string, fn: () => any, isExternal?: boolean) {
this.activeEvents.push({
[isExternal ? "EXTERNAL_" + event : event]: fn
});
if (isExternal) ipcMain.on(event, fn);
}
Following the event listener, I call an update function which (temporarily) delays execution for 7 seconds. After that, I emit an event:
splashScreen.emit(splashScreen.events.UPDATE_CHECK_FINISHED, true);
where emit is:
emit(event: string, isExternal?: Boolean) {
this.activeEvents.forEach(e => {
if (!Object.keys(e)[0].startsWith("EXTERNAL_")) e[event].bind(this).call(); //If it's an external event (i.e. `ipcMain`) then don't run callback
});
if (isExternal) this.splashScreen.webContents.send(event);
}
On the React side, in componentDidMount I have:
ipc.on("OL-CHECKED_FOR_UPDATES", () => {
ipc.send("OL-APP_START_SPLASH_CLOSE");
});
This will signal the splash screen to close only when updates have finished being checked for. When I emit the OL-CHECKED_FOR_UPDATES event this event listener should be called however it isn't.
I am able to send events fine (ipc.send works on it's own) but no matter what I do, I can't get it to listen for events.
I know these are being fired because if I move the event listener into any other script (ie index.ts) the event listener will fire, so it is only there it won't fire.
What could be the reason for this?
Just needed to sleep on it. Of course, if I create two different windows, they're going to have different render processes so when I emit an event on one, I'm not going to be able to receive it on the other.
All I had to do was send the event from the main window (the one displaying the react app) instead of the splash screen.
I'm trying out TestCafe for an AngularJS (v1.6) application.
I have a button then when clicked, opens a modal (from UI bootstrap). This works fine when I try myself in Chrome.
<button class="btn" ng-click="open()">Open</button>
Our application requires user authentication, and the login page is not Angular-based. That phase of my test works fine.
However, when the actual test runs, it "clicks" the button but nothing happens.
I suspect, but can't prove, that it's clicked before AngularJS has properly initialized on the page.
With some research, I found the testcafe-angular-selectors project and a waitForAngular method but that appears to apply only to Angular2+.
import { Role, Selector } from 'testcafe';
const regularAccUser = Role('http://127.0.0.1:8080', async t => {
await t
.typeText('[name=username]', 'abc')
.typeText('[name=password]', '123')
.click('.btn-primary');
});
fixture`Characters Modal`;
test('modal title', async t => {
await t
.useRole(regularAccUser)
.navigateTo('http://127.0.0.1:8080/fake/page')
.click('.btn')
.expect(Selector('.modal-title').innerText).eql('Insert Symbol');
});
Adding .wait(1000) before the click solves the issue. It's not waiting for Angular to load. I'd rather not have waits in every test - is there some other technique I can use?
You can use TestCafe assertions as a mechanism to wait until an element is ready before acting on it.
A typical waiting mechanism would be:
const button = Selector('button.btn')
.with({visibilityCheck: true});
await t
.expect(button.exists) // wait until component is mounted in DOM
.ok({timeout: 10000}) // wait enough time
.hover(button) // move TestCafe cursor over the component
.expect(button.hasAttribute('disabled'))
.notOk({timeout: 10000}) // wait until the button is enabled
.click(button); // now we are sure the button is there and is clickable
This article may also help you in managing all those waiting mechanisms.
As you correctly mentioned, the waitForAngular method is intended for Angular only, not for AngularJS.
I recommend you create your own waitForAngularJS function and call it on the beforeEach hook and after the role was initialized.
In the simplest case, it can be implemented as follows:
function waitForAngularJS (t) {
await t.wait(1000);
}
fixture `App tests`
.page('page with angularjs')
.beforeEach(async t => {
await waitForAngularJS(t);
});
However, the use of the wait method is not a solid solution. I recommend you find a way to detect if AngularJS is loaded on a page on the client side. If it is possible, you can implement the waitForAngularJS method using the TestCafe ClientFunctions mechanism.
This post can be usefulĀ as well: How to check if angular is loaded correctly
The justification
In my BB app, I allow rapid input from users which gets queued & sent off periodically in the background to the server. The problem I currently have is if a user leaves the page they effectively discard any pending changes sitting in the queue.
So basically what I want to do is inform the user before they leave to give them the opportunity to wait for the changes to be saved rather than just exiting & discarding.
The nitty gritty
So for the general cases where the user refreshes or attempts to navigate to an external URL we can handle the onbeforeunload event. Where it becomes slightly tricky is when we are in the context of an SPA whereby switching between pages does not cause a page refresh.
My immediate thought was to use a global click event handler for all anchors and validate whether or not I want to allow the click, which would work for in-site link navigation. However, where this falls over is navigating via the browsers Back/Forward buttons.
I also had a look at Backbone.routefilter, which at first glance appeared to do exactly what I needed. However, using the simple case as described in the docs, the route was still being executed.
The question
How do we intercept navigation for all scenarios within a Backbone SPA?
Direct link navigation
Use a global event handler to capture all click events
$(document).on('click', 'a[href^="/"]', function (e) {
var href = $(e.currentTarget).attr('href');
e.preventDefault();
if (doSomeValidation()) {
router.navigate(href, { trigger: true });
}
});
Page refreshing / external URL navigation
Handle the onbeforeunload event on the window
$(window).on('beforeunload', function (e) {
if (!doSomeValidation()) {
return 'Leaving now will may result in data loss';
}
});
Browser back/forward button navigation
Behind the scenes Backbone.Router uses the Backbone.history which ultimately leverages the HTML5 pushstate API. Depending on what options you pass to Backbone.history.start, and what your browser is capable of, the API will hook into either the onhashchange event or the onpopstate event.
Delving into the source for Backbone.history.start it becomes apparent that regardless of whether you are using push state or not, the same event handler is used i.e. checkUrl.
if (this._hasPushState) {
addEventListener('popstate', this.checkUrl, false);
} else if (this._wantsHashChange && this._hasHashChange && !this.iframe) {
addEventListener('hashchange', this.checkUrl, false);
} else if (this._wantsHashChange) {
this._checkUrlInterval = setInterval(this.checkUrl, this.interval);
}
Therefore, we can override this method & perform our validation in there
var originalCheckUrl = Backbone.history.checkUrl;
Backbone.history.checkUrl = function (e) {
if (doSomeValidation()) {
return originalCheckUrl.call(this, e);
} else {
// re-push the current page into the history (at this stage it's been popped)
window.history.pushState({}, document.title, Backbone.history.fragment);
// cancel the original event
return false;
}
};
Is it OK to stop current thread until user click some buttons in Silverlight? E.g.
var clicked = false;
ShowDialog(EventOnClosed => clicked = true);
while (!clicked) {};
return;
P.S. I know the right way. I'm just curious if there's a way to stop and then continue Silverlight execution flow.
P.P.S. Just to be more specific. Imagine a project where javascript alert() is used for messages. How do you replace "return Invoke("alert('')")" with Silverlight messageboxes? Do you change all 500 places to use the correct async technique?
No it isn't and you certainly won't want to tie up the CPU like that even if you do.
In Silverlight you really need to get used to programming in an asynchronous way.
void SomeContainingFunc(Action callBack)
{
ShowDialog(EventOnClosed => callBack()))
}
Whatever calls that and wants code to continue after the async operation is completed:-
void SomeCaller()
{
// ... do some intial stuff here
Action callBack = () =>
{
//.. code to happen after async call completed
}
SomeContainingFunc(callBack);
}
What you are attempting to do is halt (or more specifically pause) the UI thread - this is not a good idea in any circumstance, especially if you are waiting for the user to click something!
Just do it the "right way".